Skip to content

Commit

Permalink
Implement bit32.byteswap (#1075)
Browse files Browse the repository at this point in the history
I've decided to take a stab at implementing `bit32.byteswap` from the
[recently merged
RFC](https://github.com/Roblox/luau/blob/master/rfcs/function-bit32-byteswap.md).
I asked on Discord for some guidance, but for the sake of posterity:
this is my first time doing this and I am likely to have made some
mistakes.

The biggest gaps in this implementation are the lack of tests and the
lack of native codegen support. I'd appreciate help with those since I'm
not sure what's relevant for me to touch for tests, and I'm told that
relevant assembler instructions don't exist publicly yet. Intuition
tells me that Luau-side tests would go into
`tests/conformance/bitwise.luau` but this is not well documented and I'm
not sure how I'm meant to test built-in implementations.

The current implementation compiles down to `bswap` and `rev` on x86 and
ARM respectively when optimized.

---------

Co-authored-by: Arseny Kapoulkine <[email protected]>
  • Loading branch information
Dekkonot and zeux authored Oct 23, 2023
1 parent fd6250c commit 011c1af
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 0 deletions.
1 change: 1 addition & 0 deletions Analysis/src/EmbeddedBuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ declare bit32: {
replace: (n: number, v: number, field: number, width: number?) -> number,
countlz: (n: number) -> number,
countrz: (n: number) -> number,
byteswap: (n: number) -> number,
}
declare math: {
Expand Down
1 change: 1 addition & 0 deletions CodeGen/src/OptimizeConstProp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid
case LBF_GETMETATABLE:
case LBF_TONUMBER:
case LBF_TOSTRING:
case LBF_BIT32_BYTESWAP:
break;
case LBF_TABLE_INSERT:
state.invalidateHeap();
Expand Down
3 changes: 3 additions & 0 deletions Common/include/Luau/Bytecode.h
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,9 @@ enum LuauBuiltinFunction
// tonumber/tostring
LBF_TONUMBER,
LBF_TOSTRING,

// bit32.byteswap(n)
LBF_BIT32_BYTESWAP,
};

// Capture type, used in LOP_CAPTURE
Expand Down
7 changes: 7 additions & 0 deletions Compiler/src/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "Luau/Bytecode.h"
#include "Luau/Compiler.h"

LUAU_FASTFLAGVARIABLE(LuauBit32ByteswapBuiltin, false)

namespace Luau
{
namespace Compile
Expand Down Expand Up @@ -166,6 +168,8 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
return LBF_BIT32_COUNTLZ;
if (builtin.method == "countrz")
return LBF_BIT32_COUNTRZ;
if (FFlag::LuauBit32ByteswapBuiltin && builtin.method == "byteswap")
return LBF_BIT32_BYTESWAP;
}

if (builtin.object == "string")
Expand Down Expand Up @@ -402,6 +406,9 @@ BuiltinInfo getBuiltinInfo(int bfid)

case LBF_TOSTRING:
return {1, 1};

case LBF_BIT32_BYTESWAP:
return {1, 1, BuiltinInfo::Flag_NoneSafe};
};

LUAU_UNREACHABLE();
Expand Down
15 changes: 15 additions & 0 deletions VM/src/lbitlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "lcommon.h"
#include "lnumutils.h"

LUAU_FASTFLAGVARIABLE(LuauBit32Byteswap, false)

#define ALLONES ~0u
#define NBITS int(8 * sizeof(unsigned))

Expand Down Expand Up @@ -210,6 +212,18 @@ static int b_countrz(lua_State* L)
return 1;
}

static int b_swap(lua_State* L)
{
if (!FFlag::LuauBit32Byteswap)
luaL_error(L, "bit32.byteswap isn't enabled");

b_uint n = luaL_checkunsigned(L, 1);
n = (n << 24) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | (n >> 24);

lua_pushunsigned(L, n);
return 1;
}

static const luaL_Reg bitlib[] = {
{"arshift", b_arshift},
{"band", b_and},
Expand All @@ -225,6 +239,7 @@ static const luaL_Reg bitlib[] = {
{"rshift", b_rshift},
{"countlz", b_countlz},
{"countrz", b_countrz},
{"byteswap", b_swap},
{NULL, NULL},
};

Expand Down
19 changes: 19 additions & 0 deletions VM/src/lbuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,6 +1319,23 @@ static int luauF_tostring(lua_State* L, StkId res, TValue* arg0, int nresults, S
return -1;
}

static int luauF_byteswap(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisnumber(arg0))
{
double a1 = nvalue(arg0);
unsigned n;
luai_num2unsigned(n, a1);

n = (n << 24) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | (n >> 24);

setnvalue(res, double(n));
return 1;
}

return -1;
}

static int luauF_missing(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
return -1;
Expand Down Expand Up @@ -1486,6 +1503,8 @@ const luau_FastFunction luauF_table[256] = {
luauF_tonumber,
luauF_tostring,

luauF_byteswap,

// When adding builtins, add them above this line; what follows is 64 "dummy" entries with luauF_missing fallback.
// This is important so that older versions of the runtime that don't support newer builtins automatically fall back via luauF_missing.
// Given the builtin addition velocity this should always provide a larger compatibility window than bytecode versions suggest.
Expand Down
1 change: 1 addition & 0 deletions tests/Conformance.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ TEST_CASE("GC")

TEST_CASE("Bitwise")
{
ScopedFastFlag sffs{"LuauBit32Byteswap", true};
runConformance("bitwise.lua");
}

Expand Down
6 changes: 6 additions & 0 deletions tests/conformance/bitwise.lua
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ assert(bit32.countrz(0x80000000) == 31)
assert(bit32.countrz(0x40000000) == 30)
assert(bit32.countrz(0x7fffffff) == 0)

-- testing byteswap
assert(bit32.byteswap(0x10203040) == 0x40302010)
assert(bit32.byteswap(0) == 0)
assert(bit32.byteswap(-1) == 0xffffffff)

--[[
This test verifies a fix in luauF_replace() where if the 4th
parameter was not a number, but the first three are numbers, it will
Expand Down Expand Up @@ -164,5 +169,6 @@ assert(bit32.btest("1", 3) == true)
assert(bit32.countlz("42") == 26)
assert(bit32.countrz("42") == 1)
assert(bit32.extract("42", 1, 3) == 5)
assert(bit32.byteswap("0xa1b2c3d4") == 0xd4c3b2a1)

return('OK')

0 comments on commit 011c1af

Please sign in to comment.