#pragma once
#include "Luau/IrBuilder.h"
static const char* kUserdataRunTypes[] = {"extra", "color", "vec2", "mat3", "vertex", nullptr};
constexpr uint8_t kUserdataExtra = 0;
constexpr uint8_t kUserdataColor = 1;
constexpr uint8_t kUserdataVec2 = 2;
constexpr uint8_t kUserdataMat3 = 3;
constexpr uint8_t kUserdataVertex = 4;
constexpr uint8_t kTagVec2 = 12;
constexpr uint8_t kTagVertex = 13;
struct Vec2
{
float x;
float y;
};
struct Vertex
{
float pos[3];
float normal[3];
float uv[2];
};
inline bool compareMemberName(const char* member, size_t memberLength, const char* str)
{
return memberLength == strlen(str) && strcmp(member, str) == 0;
}
inline uint8_t typeToUserdataIndex(uint8_t type)
{
return type - LBC_TYPE_TAGGED_USERDATA_BASE;
}
inline uint8_t userdataIndexToType(uint8_t userdataIndex)
{
return LBC_TYPE_TAGGED_USERDATA_BASE + userdataIndex;
}
inline uint8_t vectorAccessBytecodeType(const char* member, size_t memberLength)
{
using namespace Luau::CodeGen;
if (compareMemberName(member, memberLength, "Magnitude"))
return LBC_TYPE_NUMBER;
if (compareMemberName(member, memberLength, "Unit"))
return LBC_TYPE_VECTOR;
return LBC_TYPE_ANY;
}
inline bool vectorAccess(Luau::CodeGen::IrBuilder& build, const char* member, size_t memberLength, int resultReg, int sourceReg, int pcpos)
{
using namespace Luau::CodeGen;
if (compareMemberName(member, memberLength, "Magnitude"))
{
IrOp x = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(0));
IrOp y = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(4));
IrOp z = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(8));
IrOp x2 = build.inst(IrCmd::MUL_FLOAT, x, x);
IrOp y2 = build.inst(IrCmd::MUL_FLOAT, y, y);
IrOp z2 = build.inst(IrCmd::MUL_FLOAT, z, z);
IrOp sum = build.inst(IrCmd::ADD_FLOAT, build.inst(IrCmd::ADD_FLOAT, x2, y2), z2);
IrOp mag = build.inst(IrCmd::SQRT_FLOAT, sum);
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(resultReg), build.inst(IrCmd::FLOAT_TO_NUM, mag));
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TNUMBER));
return true;
}
if (compareMemberName(member, memberLength, "Unit"))
{
IrOp x = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(0));
IrOp y = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(4));
IrOp z = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(8));
IrOp x2 = build.inst(IrCmd::MUL_FLOAT, x, x);
IrOp y2 = build.inst(IrCmd::MUL_FLOAT, y, y);
IrOp z2 = build.inst(IrCmd::MUL_FLOAT, z, z);
IrOp sum = build.inst(IrCmd::ADD_FLOAT, build.inst(IrCmd::ADD_FLOAT, x2, y2), z2);
IrOp mag = build.inst(IrCmd::SQRT_FLOAT, sum);
IrOp inv = build.inst(IrCmd::DIV_FLOAT, build.constDouble(1.0f), mag);
IrOp xr = build.inst(IrCmd::MUL_FLOAT, x, inv);
IrOp yr = build.inst(IrCmd::MUL_FLOAT, y, inv);
IrOp zr = build.inst(IrCmd::MUL_FLOAT, z, inv);
build.inst(IrCmd::STORE_VECTOR, build.vmReg(resultReg), xr, yr, zr);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TVECTOR));
return true;
}
return false;
}
inline uint8_t vectorNamecallBytecodeType(const char* member, size_t memberLength)
{
if (compareMemberName(member, memberLength, "Dot"))
return LBC_TYPE_NUMBER;
if (compareMemberName(member, memberLength, "Cross"))
return LBC_TYPE_VECTOR;
return LBC_TYPE_ANY;
}
inline bool vectorNamecall(
Luau::CodeGen::IrBuilder& build,
const char* member,
size_t memberLength,
int argResReg,
int sourceReg,
int params,
int results,
int pcpos
)
{
using namespace Luau::CodeGen;
if (compareMemberName(member, memberLength, "Dot") && params == 2 && results <= 1)
{
build.loadAndCheckTag(build.vmReg(argResReg + 2), LUA_TVECTOR, build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(0));
IrOp x2 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(argResReg + 2), build.constInt(0));
IrOp xx = build.inst(IrCmd::MUL_FLOAT, x1, x2);
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(4));
IrOp y2 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(argResReg + 2), build.constInt(4));
IrOp yy = build.inst(IrCmd::MUL_FLOAT, y1, y2);
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(8));
IrOp z2 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(argResReg + 2), build.constInt(8));
IrOp zz = build.inst(IrCmd::MUL_FLOAT, z1, z2);
IrOp sum = build.inst(IrCmd::ADD_FLOAT, build.inst(IrCmd::ADD_FLOAT, xx, yy), zz);
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(argResReg), build.inst(IrCmd::FLOAT_TO_NUM, sum));
build.inst(IrCmd::STORE_TAG, build.vmReg(argResReg), build.constTag(LUA_TNUMBER));
if (results == LUA_MULTRET)
build.inst(IrCmd::ADJUST_STACK_TO_REG, build.vmReg(argResReg), build.constInt(1));
return true;
}
if (compareMemberName(member, memberLength, "Cross") && params == 2 && results <= 1)
{
build.loadAndCheckTag(build.vmReg(argResReg + 2), LUA_TVECTOR, build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(0));
IrOp x2 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(argResReg + 2), build.constInt(0));
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(4));
IrOp y2 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(argResReg + 2), build.constInt(4));
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(sourceReg), build.constInt(8));
IrOp z2 = build.inst(IrCmd::LOAD_FLOAT, build.vmReg(argResReg + 2), build.constInt(8));
IrOp y1z2 = build.inst(IrCmd::MUL_FLOAT, y1, z2);
IrOp z1y2 = build.inst(IrCmd::MUL_FLOAT, z1, y2);
IrOp xr = build.inst(IrCmd::SUB_FLOAT, y1z2, z1y2);
IrOp z1x2 = build.inst(IrCmd::MUL_FLOAT, z1, x2);
IrOp x1z2 = build.inst(IrCmd::MUL_FLOAT, x1, z2);
IrOp yr = build.inst(IrCmd::SUB_FLOAT, z1x2, x1z2);
IrOp x1y2 = build.inst(IrCmd::MUL_FLOAT, x1, y2);
IrOp y1x2 = build.inst(IrCmd::MUL_FLOAT, y1, x2);
IrOp zr = build.inst(IrCmd::SUB_FLOAT, x1y2, y1x2);
build.inst(IrCmd::STORE_VECTOR, build.vmReg(argResReg), xr, yr, zr);
build.inst(IrCmd::STORE_TAG, build.vmReg(argResReg), build.constTag(LUA_TVECTOR));
if (results == LUA_MULTRET)
build.inst(IrCmd::ADJUST_STACK_TO_REG, build.vmReg(argResReg), build.constInt(1));
return true;
}
return false;
}
inline uint8_t userdataAccessBytecodeType(uint8_t type, const char* member, size_t memberLength)
{
switch (typeToUserdataIndex(type))
{
case kUserdataColor:
if (compareMemberName(member, memberLength, "R"))
return LBC_TYPE_NUMBER;
if (compareMemberName(member, memberLength, "G"))
return LBC_TYPE_NUMBER;
if (compareMemberName(member, memberLength, "B"))
return LBC_TYPE_NUMBER;
break;
case kUserdataVec2:
if (compareMemberName(member, memberLength, "X"))
return LBC_TYPE_NUMBER;
if (compareMemberName(member, memberLength, "Y"))
return LBC_TYPE_NUMBER;
if (compareMemberName(member, memberLength, "Magnitude"))
return LBC_TYPE_NUMBER;
if (compareMemberName(member, memberLength, "Unit"))
return userdataIndexToType(kUserdataVec2);
break;
case kUserdataMat3:
if (compareMemberName(member, memberLength, "Row1"))
return LBC_TYPE_VECTOR;
if (compareMemberName(member, memberLength, "Row2"))
return LBC_TYPE_VECTOR;
if (compareMemberName(member, memberLength, "Row3"))
return LBC_TYPE_VECTOR;
break;
case kUserdataVertex:
if (compareMemberName(member, memberLength, "pos"))
return LBC_TYPE_VECTOR;
if (compareMemberName(member, memberLength, "normal"))
return LBC_TYPE_VECTOR;
if (compareMemberName(member, memberLength, "uv"))
return userdataIndexToType(kUserdataVec2);
break;
}
return LBC_TYPE_ANY;
}
inline bool userdataAccess(
Luau::CodeGen::IrBuilder& build,
uint8_t type,
const char* member,
size_t memberLength,
int resultReg,
int sourceReg,
int pcpos
)
{
using namespace Luau::CodeGen;
switch (typeToUserdataIndex(type))
{
case kUserdataColor:
break;
case kUserdataVec2:
if (compareMemberName(member, memberLength, "X"))
{
IrOp udata = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp value = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
value = build.inst(IrCmd::FLOAT_TO_NUM, value);
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(resultReg), value);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TNUMBER));
return true;
}
if (compareMemberName(member, memberLength, "Y"))
{
IrOp udata = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp value = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
value = build.inst(IrCmd::FLOAT_TO_NUM, value);
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(resultReg), value);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TNUMBER));
return true;
}
if (compareMemberName(member, memberLength, "Magnitude"))
{
IrOp udata = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp x = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp y = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp x2 = build.inst(IrCmd::MUL_FLOAT, x, x);
IrOp y2 = build.inst(IrCmd::MUL_FLOAT, y, y);
IrOp sum = build.inst(IrCmd::ADD_FLOAT, x2, y2);
IrOp mag = build.inst(IrCmd::SQRT_FLOAT, sum);
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(resultReg), build.inst(IrCmd::FLOAT_TO_NUM, mag));
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TNUMBER));
return true;
}
if (compareMemberName(member, memberLength, "Unit"))
{
IrOp udata = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp x = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp y = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp x2 = build.inst(IrCmd::MUL_FLOAT, x, x);
IrOp y2 = build.inst(IrCmd::MUL_FLOAT, y, y);
IrOp sum = build.inst(IrCmd::ADD_FLOAT, x2, y2);
IrOp mag = build.inst(IrCmd::SQRT_FLOAT, sum);
IrOp inv = build.inst(IrCmd::DIV_FLOAT, build.constDouble(1.0), mag);
IrOp xr = build.inst(IrCmd::MUL_FLOAT, x, inv);
IrOp yr = build.inst(IrCmd::MUL_FLOAT, y, inv);
build.inst(IrCmd::CHECK_GC);
IrOp udatar = build.inst(IrCmd::NEW_USERDATA, build.constInt(sizeof(Vec2)), build.constInt(kTagVec2));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, x)), xr, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, y)), yr, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::STORE_POINTER, build.vmReg(resultReg), udatar);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TUSERDATA));
return true;
}
break;
case kUserdataMat3:
break;
case kUserdataVertex:
if (compareMemberName(member, memberLength, "pos"))
{
IrOp udata = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata, build.constInt(kTagVertex), build.vmExit(pcpos));
IrOp x = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vertex, pos[0])), build.constTag(LUA_TUSERDATA));
IrOp y = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vertex, pos[1])), build.constTag(LUA_TUSERDATA));
IrOp z = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vertex, pos[2])), build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::STORE_VECTOR, build.vmReg(resultReg), x, y, z);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TVECTOR));
return true;
}
if (compareMemberName(member, memberLength, "normal"))
{
IrOp udata = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata, build.constInt(kTagVertex), build.vmExit(pcpos));
IrOp x = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vertex, normal[0])), build.constTag(LUA_TUSERDATA));
IrOp y = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vertex, normal[1])), build.constTag(LUA_TUSERDATA));
IrOp z = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vertex, normal[2])), build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::STORE_VECTOR, build.vmReg(resultReg), x, y, z);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TVECTOR));
return true;
}
if (compareMemberName(member, memberLength, "uv"))
{
IrOp udata = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata, build.constInt(kTagVertex), build.vmExit(pcpos));
IrOp x = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vertex, uv[0])), build.constTag(LUA_TUSERDATA));
IrOp y = build.inst(IrCmd::BUFFER_READF32, udata, build.constInt(offsetof(Vertex, uv[1])), build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::CHECK_GC);
IrOp result = build.inst(IrCmd::NEW_USERDATA, build.constInt(sizeof(Vec2)), build.constInt(kTagVec2));
build.inst(IrCmd::BUFFER_WRITEF32, result, build.constInt(offsetof(Vec2, x)), x, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::BUFFER_WRITEF32, result, build.constInt(offsetof(Vec2, y)), y, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::STORE_POINTER, build.vmReg(resultReg), result);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TUSERDATA));
return true;
}
break;
}
return false;
}
inline uint8_t userdataMetamethodBytecodeType(uint8_t lhsTy, uint8_t rhsTy, Luau::CodeGen::HostMetamethod method)
{
switch (method)
{
case Luau::CodeGen::HostMetamethod::Add:
case Luau::CodeGen::HostMetamethod::Sub:
case Luau::CodeGen::HostMetamethod::Mul:
case Luau::CodeGen::HostMetamethod::Div:
if (typeToUserdataIndex(lhsTy) == kUserdataVec2 || typeToUserdataIndex(rhsTy) == kUserdataVec2)
return userdataIndexToType(kUserdataVec2);
break;
case Luau::CodeGen::HostMetamethod::Minus:
if (typeToUserdataIndex(lhsTy) == kUserdataVec2)
return userdataIndexToType(kUserdataVec2);
break;
default:
break;
}
return LBC_TYPE_ANY;
}
inline bool userdataMetamethod(
Luau::CodeGen::IrBuilder& build,
uint8_t lhsTy,
uint8_t rhsTy,
int resultReg,
Luau::CodeGen::IrOp lhs,
Luau::CodeGen::IrOp rhs,
Luau::CodeGen::HostMetamethod method,
int pcpos
)
{
using namespace Luau::CodeGen;
switch (method)
{
case Luau::CodeGen::HostMetamethod::Add:
if (typeToUserdataIndex(lhsTy) == kUserdataVec2 && typeToUserdataIndex(rhsTy) == kUserdataVec2)
{
build.loadAndCheckTag(lhs, LUA_TUSERDATA, build.vmExit(pcpos));
build.loadAndCheckTag(rhs, LUA_TUSERDATA, build.vmExit(pcpos));
IrOp udata1 = build.inst(IrCmd::LOAD_POINTER, lhs);
build.inst(IrCmd::CHECK_USERDATA_TAG, udata1, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp udata2 = build.inst(IrCmd::LOAD_POINTER, rhs);
build.inst(IrCmd::CHECK_USERDATA_TAG, udata2, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp x2 = build.inst(IrCmd::BUFFER_READF32, udata2, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp mx = build.inst(IrCmd::ADD_FLOAT, x1, x2);
IrOp y1 = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp y2 = build.inst(IrCmd::BUFFER_READF32, udata2, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp my = build.inst(IrCmd::ADD_FLOAT, y1, y2);
build.inst(IrCmd::CHECK_GC);
IrOp udatar = build.inst(IrCmd::NEW_USERDATA, build.constInt(sizeof(Vec2)), build.constInt(kTagVec2));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, x)), mx, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, y)), my, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::STORE_POINTER, build.vmReg(resultReg), udatar);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TUSERDATA));
return true;
}
break;
case Luau::CodeGen::HostMetamethod::Mul:
if (typeToUserdataIndex(lhsTy) == kUserdataVec2 && typeToUserdataIndex(rhsTy) == kUserdataVec2)
{
build.loadAndCheckTag(lhs, LUA_TUSERDATA, build.vmExit(pcpos));
build.loadAndCheckTag(rhs, LUA_TUSERDATA, build.vmExit(pcpos));
IrOp udata1 = build.inst(IrCmd::LOAD_POINTER, lhs);
build.inst(IrCmd::CHECK_USERDATA_TAG, udata1, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp udata2 = build.inst(IrCmd::LOAD_POINTER, rhs);
build.inst(IrCmd::CHECK_USERDATA_TAG, udata2, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp x2 = build.inst(IrCmd::BUFFER_READF32, udata2, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp mx = build.inst(IrCmd::MUL_FLOAT, x1, x2);
IrOp y1 = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp y2 = build.inst(IrCmd::BUFFER_READF32, udata2, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp my = build.inst(IrCmd::MUL_FLOAT, y1, y2);
build.inst(IrCmd::CHECK_GC);
IrOp udatar = build.inst(IrCmd::NEW_USERDATA, build.constInt(sizeof(Vec2)), build.constInt(kTagVec2));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, x)), mx, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, y)), my, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::STORE_POINTER, build.vmReg(resultReg), udatar);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TUSERDATA));
return true;
}
break;
case Luau::CodeGen::HostMetamethod::Minus:
if (typeToUserdataIndex(lhsTy) == kUserdataVec2)
{
build.loadAndCheckTag(lhs, LUA_TUSERDATA, build.vmExit(pcpos));
IrOp udata1 = build.inst(IrCmd::LOAD_POINTER, lhs);
build.inst(IrCmd::CHECK_USERDATA_TAG, udata1, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp x = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp y = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp mx = build.inst(IrCmd::UNM_FLOAT, x);
IrOp my = build.inst(IrCmd::UNM_FLOAT, y);
build.inst(IrCmd::CHECK_GC);
IrOp udatar = build.inst(IrCmd::NEW_USERDATA, build.constInt(sizeof(Vec2)), build.constInt(kTagVec2));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, x)), mx, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, y)), my, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::STORE_POINTER, build.vmReg(resultReg), udatar);
build.inst(IrCmd::STORE_TAG, build.vmReg(resultReg), build.constTag(LUA_TUSERDATA));
return true;
}
break;
default:
break;
}
return false;
}
inline uint8_t userdataNamecallBytecodeType(uint8_t type, const char* member, size_t memberLength)
{
switch (typeToUserdataIndex(type))
{
case kUserdataColor:
break;
case kUserdataVec2:
if (compareMemberName(member, memberLength, "Dot"))
return LBC_TYPE_NUMBER;
if (compareMemberName(member, memberLength, "Min"))
return userdataIndexToType(kUserdataVec2);
break;
case kUserdataMat3:
break;
}
return LBC_TYPE_ANY;
}
inline bool userdataNamecall(
Luau::CodeGen::IrBuilder& build,
uint8_t type,
const char* member,
size_t memberLength,
int argResReg,
int sourceReg,
int params,
int results,
int pcpos
)
{
using namespace Luau::CodeGen;
switch (typeToUserdataIndex(type))
{
case kUserdataColor:
break;
case kUserdataVec2:
if (compareMemberName(member, memberLength, "Dot"))
{
IrOp udata1 = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata1, build.constInt(kTagVec2), build.vmExit(pcpos));
build.loadAndCheckTag(build.vmReg(argResReg + 2), LUA_TUSERDATA, build.vmExit(pcpos));
IrOp udata2 = build.inst(IrCmd::LOAD_POINTER, build.vmReg(argResReg + 2));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata2, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp x2 = build.inst(IrCmd::BUFFER_READF32, udata2, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
x1 = build.inst(IrCmd::FLOAT_TO_NUM, x1);
x2 = build.inst(IrCmd::FLOAT_TO_NUM, x2);
IrOp xx = build.inst(IrCmd::MUL_NUM, x1, x2);
IrOp y1 = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp y2 = build.inst(IrCmd::BUFFER_READF32, udata2, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
y1 = build.inst(IrCmd::FLOAT_TO_NUM, y1);
y2 = build.inst(IrCmd::FLOAT_TO_NUM, y2);
IrOp yy = build.inst(IrCmd::MUL_NUM, y1, y2);
IrOp sum = build.inst(IrCmd::ADD_NUM, xx, yy);
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(argResReg), sum);
build.inst(IrCmd::STORE_TAG, build.vmReg(argResReg), build.constTag(LUA_TNUMBER));
if (results == LUA_MULTRET)
build.inst(IrCmd::ADJUST_STACK_TO_REG, build.vmReg(argResReg), build.constInt(1));
return true;
}
if (compareMemberName(member, memberLength, "Min"))
{
IrOp udata1 = build.inst(IrCmd::LOAD_POINTER, build.vmReg(sourceReg));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata1, build.constInt(kTagVec2), build.vmExit(pcpos));
build.loadAndCheckTag(build.vmReg(argResReg + 2), LUA_TUSERDATA, build.vmExit(pcpos));
IrOp udata2 = build.inst(IrCmd::LOAD_POINTER, build.vmReg(argResReg + 2));
build.inst(IrCmd::CHECK_USERDATA_TAG, udata2, build.constInt(kTagVec2), build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
IrOp x2 = build.inst(IrCmd::BUFFER_READF32, udata2, build.constInt(offsetof(Vec2, x)), build.constTag(LUA_TUSERDATA));
x1 = build.inst(IrCmd::FLOAT_TO_NUM, x1);
x2 = build.inst(IrCmd::FLOAT_TO_NUM, x2);
IrOp mx = build.inst(IrCmd::MIN_NUM, x1, x2);
IrOp y1 = build.inst(IrCmd::BUFFER_READF32, udata1, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
IrOp y2 = build.inst(IrCmd::BUFFER_READF32, udata2, build.constInt(offsetof(Vec2, y)), build.constTag(LUA_TUSERDATA));
y1 = build.inst(IrCmd::FLOAT_TO_NUM, y1);
y2 = build.inst(IrCmd::FLOAT_TO_NUM, y2);
IrOp my = build.inst(IrCmd::MIN_NUM, y1, y2);
mx = build.inst(IrCmd::NUM_TO_FLOAT, mx);
my = build.inst(IrCmd::NUM_TO_FLOAT, my);
build.inst(IrCmd::CHECK_GC);
IrOp udatar = build.inst(IrCmd::NEW_USERDATA, build.constInt(sizeof(Vec2)), build.constInt(kTagVec2));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, x)), mx, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::BUFFER_WRITEF32, udatar, build.constInt(offsetof(Vec2, y)), my, build.constTag(LUA_TUSERDATA));
build.inst(IrCmd::STORE_POINTER, build.vmReg(argResReg), udatar);
build.inst(IrCmd::STORE_TAG, build.vmReg(argResReg), build.constTag(LUA_TUSERDATA));
if (results == LUA_MULTRET)
build.inst(IrCmd::ADJUST_STACK_TO_REG, build.vmReg(argResReg), build.constInt(1));
return true;
}
break;
case kUserdataMat3:
break;
}
return false;
}