luau/tests/IrBuilder.test.cpp

224 lines
6.3 KiB
C++
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/IrBuilder.h"
#include "Luau/IrAnalysis.h"
#include "Luau/IrDump.h"
#include "Luau/OptimizeFinalX64.h"
#include "doctest.h"
using namespace Luau::CodeGen;
class IrBuilderFixture
{
public:
IrBuilder build;
};
TEST_SUITE_BEGIN("Optimization");
TEST_CASE_FIXTURE(IrBuilderFixture, "FinalX64OptCheckTag")
{
IrOp block = build.block(IrBlockKind::Internal);
IrOp fallback = build.block(IrBlockKind::Fallback);
build.beginBlock(block);
IrOp tag1 = build.inst(IrCmd::LOAD_TAG, build.vmReg(2));
build.inst(IrCmd::CHECK_TAG, tag1, build.constTag(0), fallback);
IrOp tag2 = build.inst(IrCmd::LOAD_TAG, build.vmConst(5));
build.inst(IrCmd::CHECK_TAG, tag2, build.constTag(0), fallback);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(fallback);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
updateUseCounts(build.function);
optimizeMemoryOperandsX64(build.function);
// Load from memory is 'inlined' into CHECK_TAG
CHECK("\n" + toString(build.function, /* includeDetails */ false) == R"(
bb_0:
CHECK_TAG R2, tnil, bb_fallback_1
CHECK_TAG K5, tnil, bb_fallback_1
LOP_RETURN 0u
bb_fallback_1:
LOP_RETURN 0u
)");
}
TEST_CASE_FIXTURE(IrBuilderFixture, "FinalX64OptBinaryArith")
{
IrOp block = build.block(IrBlockKind::Internal);
build.beginBlock(block);
IrOp opA = build.inst(IrCmd::LOAD_DOUBLE, build.vmReg(1));
IrOp opB = build.inst(IrCmd::LOAD_DOUBLE, build.vmReg(2));
build.inst(IrCmd::ADD_NUM, opA, opB);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
updateUseCounts(build.function);
optimizeMemoryOperandsX64(build.function);
// Load from memory is 'inlined' into second argument
CHECK("\n" + toString(build.function, /* includeDetails */ false) == R"(
bb_0:
%0 = LOAD_DOUBLE R1
%2 = ADD_NUM %0, R2
LOP_RETURN 0u
)");
}
TEST_CASE_FIXTURE(IrBuilderFixture, "FinalX64OptEqTag1")
{
IrOp block = build.block(IrBlockKind::Internal);
IrOp trueBlock = build.block(IrBlockKind::Internal);
IrOp falseBlock = build.block(IrBlockKind::Internal);
build.beginBlock(block);
IrOp opA = build.inst(IrCmd::LOAD_TAG, build.vmReg(1));
IrOp opB = build.inst(IrCmd::LOAD_TAG, build.vmReg(2));
build.inst(IrCmd::JUMP_EQ_TAG, opA, opB, trueBlock, falseBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(trueBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(falseBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
updateUseCounts(build.function);
optimizeMemoryOperandsX64(build.function);
// Load from memory is 'inlined' into first argument
CHECK("\n" + toString(build.function, /* includeDetails */ false) == R"(
bb_0:
%1 = LOAD_TAG R2
JUMP_EQ_TAG R1, %1, bb_1, bb_2
bb_1:
LOP_RETURN 0u
bb_2:
LOP_RETURN 0u
)");
}
TEST_CASE_FIXTURE(IrBuilderFixture, "FinalX64OptEqTag2")
{
IrOp block = build.block(IrBlockKind::Internal);
IrOp trueBlock = build.block(IrBlockKind::Internal);
IrOp falseBlock = build.block(IrBlockKind::Internal);
build.beginBlock(block);
IrOp opA = build.inst(IrCmd::LOAD_TAG, build.vmReg(1));
IrOp opB = build.inst(IrCmd::LOAD_TAG, build.vmReg(2));
build.inst(IrCmd::STORE_TAG, build.vmReg(6), opA);
build.inst(IrCmd::JUMP_EQ_TAG, opA, opB, trueBlock, falseBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(trueBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(falseBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
updateUseCounts(build.function);
optimizeMemoryOperandsX64(build.function);
// Load from memory is 'inlined' into second argument is it can't be done for the first one
// We also swap first and second argument to generate memory access on the LHS
CHECK("\n" + toString(build.function, /* includeDetails */ false) == R"(
bb_0:
%0 = LOAD_TAG R1
STORE_TAG R6, %0
JUMP_EQ_TAG R2, %0, bb_1, bb_2
bb_1:
LOP_RETURN 0u
bb_2:
LOP_RETURN 0u
)");
}
TEST_CASE_FIXTURE(IrBuilderFixture, "FinalX64OptEqTag3")
{
IrOp block = build.block(IrBlockKind::Internal);
IrOp trueBlock = build.block(IrBlockKind::Internal);
IrOp falseBlock = build.block(IrBlockKind::Internal);
build.beginBlock(block);
IrOp table = build.inst(IrCmd::LOAD_POINTER, build.vmReg(1));
IrOp arrElem = build.inst(IrCmd::GET_ARR_ADDR, table, build.constUint(0));
IrOp opA = build.inst(IrCmd::LOAD_TAG, arrElem);
build.inst(IrCmd::JUMP_EQ_TAG, opA, build.constTag(0), trueBlock, falseBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(trueBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(falseBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
updateUseCounts(build.function);
optimizeMemoryOperandsX64(build.function);
// Load from memory is 'inlined' into first argument
CHECK("\n" + toString(build.function, /* includeDetails */ false) == R"(
bb_0:
%0 = LOAD_POINTER R1
%1 = GET_ARR_ADDR %0, 0u
%2 = LOAD_TAG %1
JUMP_EQ_TAG %2, tnil, bb_1, bb_2
bb_1:
LOP_RETURN 0u
bb_2:
LOP_RETURN 0u
)");
}
TEST_CASE_FIXTURE(IrBuilderFixture, "FinalX64OptJumpCmpNum")
{
IrOp block = build.block(IrBlockKind::Internal);
IrOp trueBlock = build.block(IrBlockKind::Internal);
IrOp falseBlock = build.block(IrBlockKind::Internal);
build.beginBlock(block);
IrOp opA = build.inst(IrCmd::LOAD_DOUBLE, build.vmReg(1));
IrOp opB = build.inst(IrCmd::LOAD_DOUBLE, build.vmReg(2));
build.inst(IrCmd::JUMP_CMP_NUM, opA, opB, trueBlock, falseBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(trueBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
build.beginBlock(falseBlock);
build.inst(IrCmd::LOP_RETURN, build.constUint(0));
updateUseCounts(build.function);
optimizeMemoryOperandsX64(build.function);
// Load from memory is 'inlined' into first argument
CHECK("\n" + toString(build.function, /* includeDetails */ false) == R"(
bb_0:
%1 = LOAD_DOUBLE R2
JUMP_CMP_NUM R1, %1, bb_1, bb_2
bb_1:
LOP_RETURN 0u
bb_2:
LOP_RETURN 0u
)");
}
TEST_SUITE_END();