mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-13 05:20:38 +00:00
222 lines
6 KiB
C++
222 lines
6 KiB
C++
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||
|
#include "Luau/AssemblyBuilderA64.h"
|
||
|
#include "Luau/StringUtils.h"
|
||
|
|
||
|
#include "doctest.h"
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
using namespace Luau::CodeGen;
|
||
|
|
||
|
static std::string bytecodeAsArray(const std::vector<uint8_t>& bytecode)
|
||
|
{
|
||
|
std::string result = "{";
|
||
|
|
||
|
for (size_t i = 0; i < bytecode.size(); i++)
|
||
|
Luau::formatAppend(result, "%s0x%02x", i == 0 ? "" : ", ", bytecode[i]);
|
||
|
|
||
|
return result.append("}");
|
||
|
}
|
||
|
|
||
|
static std::string bytecodeAsArray(const std::vector<uint32_t>& code)
|
||
|
{
|
||
|
std::string result = "{";
|
||
|
|
||
|
for (size_t i = 0; i < code.size(); i++)
|
||
|
Luau::formatAppend(result, "%s0x%08x", i == 0 ? "" : ", ", code[i]);
|
||
|
|
||
|
return result.append("}");
|
||
|
}
|
||
|
|
||
|
class AssemblyBuilderA64Fixture
|
||
|
{
|
||
|
public:
|
||
|
bool check(void (*f)(AssemblyBuilderA64& build), std::vector<uint32_t> code, std::vector<uint8_t> data = {})
|
||
|
{
|
||
|
AssemblyBuilderA64 build(/* logText= */ false);
|
||
|
|
||
|
f(build);
|
||
|
|
||
|
build.finalize();
|
||
|
|
||
|
if (build.code != code)
|
||
|
{
|
||
|
printf("Expected code: %s\nReceived code: %s\n", bytecodeAsArray(code).c_str(), bytecodeAsArray(build.code).c_str());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (build.data != data)
|
||
|
{
|
||
|
printf("Expected data: %s\nReceived data: %s\n", bytecodeAsArray(data).c_str(), bytecodeAsArray(build.data).c_str());
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// armconverter.com can be used to validate instruction sequences
|
||
|
TEST_SUITE_BEGIN("A64Assembly");
|
||
|
|
||
|
#define SINGLE_COMPARE(inst, ...) \
|
||
|
CHECK(check( \
|
||
|
[](AssemblyBuilderA64& build) { \
|
||
|
build.inst; \
|
||
|
}, \
|
||
|
{__VA_ARGS__}))
|
||
|
|
||
|
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "Unary")
|
||
|
{
|
||
|
SINGLE_COMPARE(neg(x0, x1), 0xCB0103E0);
|
||
|
SINGLE_COMPARE(neg(w0, w1), 0x4B0103E0);
|
||
|
|
||
|
SINGLE_COMPARE(clz(x0, x1), 0xDAC01020);
|
||
|
SINGLE_COMPARE(clz(w0, w1), 0x5AC01020);
|
||
|
SINGLE_COMPARE(rbit(x0, x1), 0xDAC00020);
|
||
|
SINGLE_COMPARE(rbit(w0, w1), 0x5AC00020);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "Binary")
|
||
|
{
|
||
|
// reg, reg
|
||
|
SINGLE_COMPARE(add(x0, x1, x2), 0x8B020020);
|
||
|
SINGLE_COMPARE(add(w0, w1, w2), 0x0B020020);
|
||
|
SINGLE_COMPARE(add(x0, x1, x2, 7), 0x8B021C20);
|
||
|
SINGLE_COMPARE(sub(x0, x1, x2), 0xCB020020);
|
||
|
SINGLE_COMPARE(and_(x0, x1, x2), 0x8A020020);
|
||
|
SINGLE_COMPARE(orr(x0, x1, x2), 0xAA020020);
|
||
|
SINGLE_COMPARE(eor(x0, x1, x2), 0xCA020020);
|
||
|
SINGLE_COMPARE(lsl(x0, x1, x2), 0x9AC22020);
|
||
|
SINGLE_COMPARE(lsl(w0, w1, w2), 0x1AC22020);
|
||
|
SINGLE_COMPARE(lsr(x0, x1, x2), 0x9AC22420);
|
||
|
SINGLE_COMPARE(asr(x0, x1, x2), 0x9AC22820);
|
||
|
SINGLE_COMPARE(ror(x0, x1, x2), 0x9AC22C20);
|
||
|
|
||
|
// reg, imm
|
||
|
SINGLE_COMPARE(add(x3, x7, 78), 0x910138E3);
|
||
|
SINGLE_COMPARE(add(w3, w7, 78), 0x110138E3);
|
||
|
SINGLE_COMPARE(sub(w3, w7, 78), 0x510138E3);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "Loads")
|
||
|
{
|
||
|
// address forms
|
||
|
SINGLE_COMPARE(ldr(x0, x1), 0xF9400020);
|
||
|
SINGLE_COMPARE(ldr(x0, AddressA64(x1, 8)), 0xF9400420);
|
||
|
SINGLE_COMPARE(ldr(x0, AddressA64(x1, x7)), 0xF8676820);
|
||
|
|
||
|
// load sizes
|
||
|
SINGLE_COMPARE(ldr(x0, x1), 0xF9400020);
|
||
|
SINGLE_COMPARE(ldr(w0, x1), 0xB9400020);
|
||
|
SINGLE_COMPARE(ldrb(w0, x1), 0x39400020);
|
||
|
SINGLE_COMPARE(ldrh(w0, x1), 0x79400020);
|
||
|
SINGLE_COMPARE(ldrsb(x0, x1), 0x39800020);
|
||
|
SINGLE_COMPARE(ldrsb(w0, x1), 0x39C00020);
|
||
|
SINGLE_COMPARE(ldrsh(x0, x1), 0x79800020);
|
||
|
SINGLE_COMPARE(ldrsh(w0, x1), 0x79C00020);
|
||
|
SINGLE_COMPARE(ldrsw(x0, x1), 0xB9800020);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "Stores")
|
||
|
{
|
||
|
// address forms
|
||
|
SINGLE_COMPARE(str(x0, x1), 0xF9000020);
|
||
|
SINGLE_COMPARE(str(x0, AddressA64(x1, 8)), 0xF9000420);
|
||
|
SINGLE_COMPARE(str(x0, AddressA64(x1, x7)), 0xF8276820);
|
||
|
|
||
|
// store sizes
|
||
|
SINGLE_COMPARE(str(x0, x1), 0xF9000020);
|
||
|
SINGLE_COMPARE(str(w0, x1), 0xB9000020);
|
||
|
SINGLE_COMPARE(strb(w0, x1), 0x39000020);
|
||
|
SINGLE_COMPARE(strh(w0, x1), 0x79000020);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "Moves")
|
||
|
{
|
||
|
SINGLE_COMPARE(mov(x0, x1), 0xAA0103E0);
|
||
|
SINGLE_COMPARE(mov(w0, w1), 0x2A0103E0);
|
||
|
SINGLE_COMPARE(mov(x0, 42), 0xD2800540);
|
||
|
SINGLE_COMPARE(mov(w0, 42), 0x52800540);
|
||
|
SINGLE_COMPARE(movk(x0, 42, 16), 0xF2A00540);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "ControlFlow")
|
||
|
{
|
||
|
// Jump back
|
||
|
CHECK(check(
|
||
|
[](AssemblyBuilderA64& build) {
|
||
|
Label start = build.setLabel();
|
||
|
build.mov(x0, x1);
|
||
|
build.b(ConditionA64::Equal, start);
|
||
|
},
|
||
|
{0xAA0103E0, 0x54FFFFE0}));
|
||
|
|
||
|
// Jump forward
|
||
|
CHECK(check(
|
||
|
[](AssemblyBuilderA64& build) {
|
||
|
Label skip;
|
||
|
build.b(ConditionA64::Equal, skip);
|
||
|
build.mov(x0, x1);
|
||
|
build.setLabel(skip);
|
||
|
},
|
||
|
{0x54000040, 0xAA0103E0}));
|
||
|
|
||
|
// Jumps
|
||
|
CHECK(check(
|
||
|
[](AssemblyBuilderA64& build) {
|
||
|
Label skip;
|
||
|
build.b(ConditionA64::Equal, skip);
|
||
|
build.cbz(x0, skip);
|
||
|
build.cbnz(x0, skip);
|
||
|
build.setLabel(skip);
|
||
|
},
|
||
|
{0x54000060, 0xB4000040, 0xB5000020}));
|
||
|
|
||
|
// Basic control flow
|
||
|
SINGLE_COMPARE(ret(), 0xD65F03C0);
|
||
|
}
|
||
|
|
||
|
TEST_CASE("LogTest")
|
||
|
{
|
||
|
AssemblyBuilderA64 build(/* logText= */ true);
|
||
|
|
||
|
build.add(w0, w1, w2);
|
||
|
build.add(x0, x1, x2, 2);
|
||
|
build.add(w7, w8, 5);
|
||
|
build.add(x7, x8, 5);
|
||
|
build.ldr(x7, x8);
|
||
|
build.ldr(x7, AddressA64(x8, 8));
|
||
|
build.ldr(x7, AddressA64(x8, x9));
|
||
|
build.mov(x1, x2);
|
||
|
build.movk(x1, 42, 16);
|
||
|
|
||
|
Label l;
|
||
|
build.b(ConditionA64::Plus, l);
|
||
|
build.cbz(x7, l);
|
||
|
|
||
|
build.setLabel(l);
|
||
|
build.ret();
|
||
|
|
||
|
build.finalize();
|
||
|
|
||
|
std::string expected = R"(
|
||
|
add w0,w1,w2
|
||
|
add x0,x1,x2 LSL #2
|
||
|
add w7,w8,#5
|
||
|
add x7,x8,#5
|
||
|
ldr x7,[x8]
|
||
|
ldr x7,[x8,#8]
|
||
|
ldr x7,[x8,x9]
|
||
|
mov x1,x2
|
||
|
movk x1,#42 LSL #16
|
||
|
b.pl .L1
|
||
|
cbz x7,.L1
|
||
|
.L1:
|
||
|
ret
|
||
|
)";
|
||
|
|
||
|
CHECK("\n" + build.text == expected);
|
||
|
}
|
||
|
|
||
|
TEST_SUITE_END();
|