mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 22:35:43 +08:00
543 lines
23 KiB
C++
543 lines
23 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
#pragma once
|
|
|
|
#include "Luau/IrBuilder.h"
|
|
|
|
static const char* kUserdataRunTypes[] = {"extra", "color", "vec2", "mat3", nullptr};
|
|
|
|
constexpr uint8_t kUserdataExtra = 0;
|
|
constexpr uint8_t kUserdataColor = 1;
|
|
constexpr uint8_t kUserdataVec2 = 2;
|
|
constexpr uint8_t kUserdataMat3 = 3;
|
|
|
|
// Userdata tags can be different from userdata bytecode type indices
|
|
constexpr uint8_t kTagVec2 = 12;
|
|
|
|
struct Vec2
|
|
{
|
|
float x;
|
|
float y;
|
|
};
|
|
|
|
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)
|
|
{
|
|
// Underflow will push the type into a value that is not comparable to any kUserdata* constants
|
|
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_NUM, x, x);
|
|
IrOp y2 = build.inst(IrCmd::MUL_NUM, y, y);
|
|
IrOp z2 = build.inst(IrCmd::MUL_NUM, z, z);
|
|
|
|
IrOp sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, x2, y2), z2);
|
|
|
|
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
|
|
|
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(resultReg), 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_NUM, x, x);
|
|
IrOp y2 = build.inst(IrCmd::MUL_NUM, y, y);
|
|
IrOp z2 = build.inst(IrCmd::MUL_NUM, z, z);
|
|
|
|
IrOp sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, x2, y2), z2);
|
|
|
|
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
|
IrOp inv = build.inst(IrCmd::DIV_NUM, build.constDouble(1.0), mag);
|
|
|
|
IrOp xr = build.inst(IrCmd::MUL_NUM, x, inv);
|
|
IrOp yr = build.inst(IrCmd::MUL_NUM, y, inv);
|
|
IrOp zr = build.inst(IrCmd::MUL_NUM, 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_NUM, 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_NUM, 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_NUM, z1, z2);
|
|
|
|
IrOp sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, xx, yy), zz);
|
|
|
|
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(argResReg), sum);
|
|
build.inst(IrCmd::STORE_TAG, build.vmReg(argResReg), build.constTag(LUA_TNUMBER));
|
|
|
|
// If the function is called in multi-return context, stack has to be adjusted
|
|
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_NUM, y1, z2);
|
|
IrOp z1y2 = build.inst(IrCmd::MUL_NUM, z1, y2);
|
|
IrOp xr = build.inst(IrCmd::SUB_NUM, y1z2, z1y2);
|
|
|
|
IrOp z1x2 = build.inst(IrCmd::MUL_NUM, z1, x2);
|
|
IrOp x1z2 = build.inst(IrCmd::MUL_NUM, x1, z2);
|
|
IrOp yr = build.inst(IrCmd::SUB_NUM, z1x2, x1z2);
|
|
|
|
IrOp x1y2 = build.inst(IrCmd::MUL_NUM, x1, y2);
|
|
IrOp y1x2 = build.inst(IrCmd::MUL_NUM, y1, x2);
|
|
IrOp zr = build.inst(IrCmd::SUB_NUM, 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 the function is called in multi-return context, stack has to be adjusted
|
|
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;
|
|
}
|
|
|
|
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));
|
|
|
|
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));
|
|
|
|
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_NUM, x, x);
|
|
IrOp y2 = build.inst(IrCmd::MUL_NUM, y, y);
|
|
|
|
IrOp sum = build.inst(IrCmd::ADD_NUM, x2, y2);
|
|
|
|
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
|
|
|
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(resultReg), 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_NUM, x, x);
|
|
IrOp y2 = build.inst(IrCmd::MUL_NUM, y, y);
|
|
|
|
IrOp sum = build.inst(IrCmd::ADD_NUM, x2, y2);
|
|
|
|
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
|
|
IrOp inv = build.inst(IrCmd::DIV_NUM, build.constDouble(1.0), mag);
|
|
|
|
IrOp xr = build.inst(IrCmd::MUL_NUM, x, inv);
|
|
IrOp yr = build.inst(IrCmd::MUL_NUM, 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;
|
|
}
|
|
|
|
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_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));
|
|
IrOp my = build.inst(IrCmd::ADD_NUM, 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_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));
|
|
IrOp my = build.inst(IrCmd::MUL_NUM, 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_NUM, x);
|
|
IrOp my = build.inst(IrCmd::UNM_NUM, 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));
|
|
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));
|
|
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 the function is called in multi-return context, stack has to be adjusted
|
|
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));
|
|
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));
|
|
IrOp my = build.inst(IrCmd::MIN_NUM, 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(argResReg), udatar);
|
|
build.inst(IrCmd::STORE_TAG, build.vmReg(argResReg), build.constTag(LUA_TUSERDATA));
|
|
|
|
// If the function is called in multi-return context, stack has to be adjusted
|
|
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;
|
|
}
|