mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-12 21:10:37 +00:00
e5ec0cdff3
## What's changed - `bit32.byteswap` added ([RFC](4f543ec23b/docs/function-bit32-byteswap.md
)) - Buffer library implementation ([RFC](4f543ec23b/docs/type-byte-buffer.md
)) - Fixed a missing `stdint.h` include - Fixed parser limiter for recursive type annotations being kind of weird (fixes #645) ### Native Codegen - Fixed a pair of issues when lowering `bit32.extract` - Fixed a narrow edge case that could result in an infinite loop without an interruption - Fixed a negative array out-of-bounds access issue - Temporarily reverted linear block predecessor value propagation ### New type solver - We now type check assignments to annotated variables - Fixed some test cases under local type inference - Moved `isPending` checks for type families to improve performance - Optimized our process for testing if a free type is sufficiently solved - Removed "none ptr" from lea instruction disassembly logging ### Build system & tooling - CMake configuration now validates dependencies to maintain separation between components - Improvements to the fuzzer coverage - Deduplicator for fuzzed callstacks --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
562 lines
16 KiB
C++
562 lines
16 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
#include "Luau/IrCallWrapperX64.h"
|
|
#include "Luau/IrRegAllocX64.h"
|
|
|
|
#include "doctest.h"
|
|
|
|
using namespace Luau::CodeGen;
|
|
using namespace Luau::CodeGen::X64;
|
|
|
|
class IrCallWrapperX64Fixture
|
|
{
|
|
public:
|
|
IrCallWrapperX64Fixture(ABIX64 abi = ABIX64::Windows)
|
|
: build(/* logText */ true, abi)
|
|
, regs(build, function, nullptr)
|
|
, callWrap(regs, build, ~0u)
|
|
{
|
|
}
|
|
|
|
void checkMatch(std::string expected)
|
|
{
|
|
regs.assertAllFree();
|
|
|
|
build.finalize();
|
|
|
|
CHECK("\n" + build.text == expected);
|
|
}
|
|
|
|
AssemblyBuilderX64 build;
|
|
IrFunction function;
|
|
IrRegAllocX64 regs;
|
|
IrCallWrapperX64 callWrap;
|
|
|
|
// Tests rely on these to force interference between registers
|
|
static constexpr RegisterX64 rArg1 = rcx;
|
|
static constexpr RegisterX64 rArg1d = ecx;
|
|
static constexpr RegisterX64 rArg2 = rdx;
|
|
static constexpr RegisterX64 rArg2d = edx;
|
|
static constexpr RegisterX64 rArg3 = r8;
|
|
static constexpr RegisterX64 rArg3d = r8d;
|
|
static constexpr RegisterX64 rArg4 = r9;
|
|
static constexpr RegisterX64 rArg4d = r9d;
|
|
};
|
|
|
|
class IrCallWrapperX64FixtureSystemV : public IrCallWrapperX64Fixture
|
|
{
|
|
public:
|
|
IrCallWrapperX64FixtureSystemV()
|
|
: IrCallWrapperX64Fixture(ABIX64::SystemV)
|
|
{
|
|
}
|
|
};
|
|
|
|
TEST_SUITE_BEGIN("IrCallWrapperX64");
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "SimpleRegs")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rax, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, tmp1);
|
|
callWrap.addArgument(SizeX64::qword, tmp2); // Already in its place
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rcx,rax
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "TrickyUse1")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, tmp1.reg); // Already in its place
|
|
callWrap.addArgument(SizeX64::qword, tmp1.release());
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rdx,rcx
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "TrickyUse2")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.reg]);
|
|
callWrap.addArgument(SizeX64::qword, tmp1.release());
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rdx,rcx
|
|
mov rcx,qword ptr [rcx]
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "SimpleMemImm")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rax, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rsi, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::dword, 32);
|
|
callWrap.addArgument(SizeX64::dword, -1);
|
|
callWrap.addArgument(SizeX64::qword, qword[r14 + 32]);
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.release() + tmp2.release()]);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov r8,qword ptr [r14+020h]
|
|
mov r9,qword ptr [rax+rsi]
|
|
mov ecx,20h
|
|
mov edx,FFFFFFFFh
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "SimpleStackArgs")
|
|
{
|
|
ScopedRegX64 tmp{regs, regs.takeReg(rax, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, tmp);
|
|
callWrap.addArgument(SizeX64::qword, qword[r14 + 16]);
|
|
callWrap.addArgument(SizeX64::qword, qword[r14 + 32]);
|
|
callWrap.addArgument(SizeX64::qword, qword[r14 + 48]);
|
|
callWrap.addArgument(SizeX64::dword, 1);
|
|
callWrap.addArgument(SizeX64::qword, qword[r13]);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rdx,qword ptr [r13]
|
|
mov qword ptr [rsp+028h],rdx
|
|
mov rcx,rax
|
|
mov rdx,qword ptr [r14+010h]
|
|
mov r8,qword ptr [r14+020h]
|
|
mov r9,qword ptr [r14+030h]
|
|
mov dword ptr [rsp+020h],1
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "FixedRegisters")
|
|
{
|
|
callWrap.addArgument(SizeX64::dword, 1);
|
|
callWrap.addArgument(SizeX64::qword, 2);
|
|
callWrap.addArgument(SizeX64::qword, 3);
|
|
callWrap.addArgument(SizeX64::qword, 4);
|
|
callWrap.addArgument(SizeX64::qword, r14);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov qword ptr [rsp+020h],r14
|
|
mov ecx,1
|
|
mov rdx,2
|
|
mov r8,3
|
|
mov r9,4
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "EasyInterference")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rdi, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rsi, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp3{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp4{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, tmp1);
|
|
callWrap.addArgument(SizeX64::qword, tmp2);
|
|
callWrap.addArgument(SizeX64::qword, tmp3);
|
|
callWrap.addArgument(SizeX64::qword, tmp4);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov r8,rdx
|
|
mov rdx,rsi
|
|
mov r9,rcx
|
|
mov rcx,rdi
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "FakeInterference")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.release() + 8]);
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp2.release() + 8]);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rcx,qword ptr [rcx+8]
|
|
mov rdx,qword ptr [rdx+8]
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "HardInterferenceInt")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg4, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg3, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp3{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp4{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, tmp1);
|
|
callWrap.addArgument(SizeX64::qword, tmp2);
|
|
callWrap.addArgument(SizeX64::qword, tmp3);
|
|
callWrap.addArgument(SizeX64::qword, tmp4);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rax,r9
|
|
mov r9,rcx
|
|
mov rcx,rax
|
|
mov rax,r8
|
|
mov r8,rdx
|
|
mov rdx,rax
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "HardInterferenceInt2")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg4d, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg3d, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp3{regs, regs.takeReg(rArg2d, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp4{regs, regs.takeReg(rArg1d, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::dword, tmp1);
|
|
callWrap.addArgument(SizeX64::dword, tmp2);
|
|
callWrap.addArgument(SizeX64::dword, tmp3);
|
|
callWrap.addArgument(SizeX64::dword, tmp4);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov eax,r9d
|
|
mov r9d,ecx
|
|
mov ecx,eax
|
|
mov eax,r8d
|
|
mov r8d,edx
|
|
mov edx,eax
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "HardInterferenceFp")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(xmm1, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(xmm0, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::xmmword, tmp1);
|
|
callWrap.addArgument(SizeX64::xmmword, tmp2);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
vmovsd xmm2,xmm1,xmm1
|
|
vmovsd xmm1,xmm0,xmm0
|
|
vmovsd xmm0,xmm2,xmm2
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "HardInterferenceBoth")
|
|
{
|
|
ScopedRegX64 int1{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
ScopedRegX64 int2{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
ScopedRegX64 fp1{regs, regs.takeReg(xmm3, kInvalidInstIdx)};
|
|
ScopedRegX64 fp2{regs, regs.takeReg(xmm2, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, int1);
|
|
callWrap.addArgument(SizeX64::qword, int2);
|
|
callWrap.addArgument(SizeX64::xmmword, fp1);
|
|
callWrap.addArgument(SizeX64::xmmword, fp2);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rax,rdx
|
|
mov rdx,rcx
|
|
mov rcx,rax
|
|
vmovsd xmm0,xmm3,xmm3
|
|
vmovsd xmm3,xmm2,xmm2
|
|
vmovsd xmm2,xmm0,xmm0
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "FakeMultiuseInterferenceMem")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.reg + tmp2.reg + 8]);
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp2.reg + 16]);
|
|
tmp1.release();
|
|
tmp2.release();
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rcx,qword ptr [rcx+rdx+8]
|
|
mov rdx,qword ptr [rdx+010h]
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "HardMultiuseInterferenceMem1")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.reg + tmp2.reg + 8]);
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.reg + 16]);
|
|
tmp1.release();
|
|
tmp2.release();
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rax,rcx
|
|
mov rcx,qword ptr [rax+rdx+8]
|
|
mov rdx,qword ptr [rax+010h]
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "HardMultiuseInterferenceMem2")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.reg + tmp2.reg + 8]);
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.reg + tmp2.reg + 16]);
|
|
tmp1.release();
|
|
tmp2.release();
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rax,rcx
|
|
mov rcx,qword ptr [rax+rdx+8]
|
|
mov rdx,qword ptr [rax+rdx+010h]
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "HardMultiuseInterferenceMem3")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg3, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp3{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.reg + tmp2.reg + 8]);
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp2.reg + tmp3.reg + 16]);
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp3.reg + tmp1.reg + 16]);
|
|
tmp1.release();
|
|
tmp2.release();
|
|
tmp3.release();
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rax,r8
|
|
mov r8,qword ptr [rcx+rax+010h]
|
|
mov rbx,rdx
|
|
mov rdx,qword ptr [rbx+rcx+010h]
|
|
mov rcx,qword ptr [rax+rbx+8]
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "InterferenceWithCallArg1")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, qword[tmp1.reg + 8]);
|
|
callWrap.call(qword[tmp1.release() + 16]);
|
|
|
|
checkMatch(R"(
|
|
mov rax,rcx
|
|
mov rcx,qword ptr [rax+8]
|
|
call qword ptr [rax+010h]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "InterferenceWithCallArg2")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, tmp2);
|
|
callWrap.call(qword[tmp1.release() + 16]);
|
|
|
|
checkMatch(R"(
|
|
mov rax,rcx
|
|
mov rcx,rdx
|
|
call qword ptr [rax+010h]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "InterferenceWithCallArg3")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, tmp1.reg);
|
|
callWrap.call(qword[tmp1.release() + 16]);
|
|
|
|
checkMatch(R"(
|
|
call qword ptr [rcx+010h]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "WithLastIrInstUse1")
|
|
{
|
|
IrInst irInst1;
|
|
IrOp irOp1 = {IrOpKind::Inst, 0};
|
|
irInst1.regX64 = regs.takeReg(xmm0, irOp1.index);
|
|
irInst1.lastUse = 1;
|
|
function.instructions.push_back(irInst1);
|
|
callWrap.instIdx = irInst1.lastUse;
|
|
|
|
callWrap.addArgument(SizeX64::xmmword, irInst1.regX64, irOp1); // Already in its place
|
|
callWrap.addArgument(SizeX64::xmmword, qword[r12 + 8]);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
vmovsd xmm1,qword ptr [r12+8]
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "WithLastIrInstUse2")
|
|
{
|
|
IrInst irInst1;
|
|
IrOp irOp1 = {IrOpKind::Inst, 0};
|
|
irInst1.regX64 = regs.takeReg(xmm0, irOp1.index);
|
|
irInst1.lastUse = 1;
|
|
function.instructions.push_back(irInst1);
|
|
callWrap.instIdx = irInst1.lastUse;
|
|
|
|
callWrap.addArgument(SizeX64::xmmword, qword[r12 + 8]);
|
|
callWrap.addArgument(SizeX64::xmmword, irInst1.regX64, irOp1);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
vmovsd xmm1,xmm0,xmm0
|
|
vmovsd xmm0,qword ptr [r12+8]
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "WithLastIrInstUse3")
|
|
{
|
|
IrInst irInst1;
|
|
IrOp irOp1 = {IrOpKind::Inst, 0};
|
|
irInst1.regX64 = regs.takeReg(xmm0, irOp1.index);
|
|
irInst1.lastUse = 1;
|
|
function.instructions.push_back(irInst1);
|
|
callWrap.instIdx = irInst1.lastUse;
|
|
|
|
callWrap.addArgument(SizeX64::xmmword, irInst1.regX64, irOp1);
|
|
callWrap.addArgument(SizeX64::xmmword, irInst1.regX64, irOp1);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
vmovsd xmm1,xmm0,xmm0
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "WithLastIrInstUse4")
|
|
{
|
|
IrInst irInst1;
|
|
IrOp irOp1 = {IrOpKind::Inst, 0};
|
|
irInst1.regX64 = regs.takeReg(rax, irOp1.index);
|
|
irInst1.lastUse = 1;
|
|
function.instructions.push_back(irInst1);
|
|
callWrap.instIdx = irInst1.lastUse;
|
|
|
|
ScopedRegX64 tmp{regs, regs.takeReg(rdx, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, r15);
|
|
callWrap.addArgument(SizeX64::qword, irInst1.regX64, irOp1);
|
|
callWrap.addArgument(SizeX64::qword, tmp);
|
|
callWrap.call(qword[r12]);
|
|
|
|
checkMatch(R"(
|
|
mov rcx,r15
|
|
mov r8,rdx
|
|
mov rdx,rax
|
|
call qword ptr [r12]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "ExtraCoverage")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
callWrap.addArgument(SizeX64::qword, addr[r12 + 8]);
|
|
callWrap.addArgument(SizeX64::qword, addr[r12 + 16]);
|
|
callWrap.addArgument(SizeX64::xmmword, xmmword[r13]);
|
|
callWrap.call(qword[tmp1.release() + tmp2.release()]);
|
|
|
|
checkMatch(R"(
|
|
vmovups xmm2,xmmword ptr [r13]
|
|
mov rax,rcx
|
|
lea rcx,[r12+8]
|
|
mov rbx,rdx
|
|
lea rdx,[r12+010h]
|
|
call qword ptr [rax+rbx]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "AddressInStackArguments")
|
|
{
|
|
callWrap.addArgument(SizeX64::dword, 1);
|
|
callWrap.addArgument(SizeX64::dword, 2);
|
|
callWrap.addArgument(SizeX64::dword, 3);
|
|
callWrap.addArgument(SizeX64::dword, 4);
|
|
callWrap.addArgument(SizeX64::qword, addr[r12 + 16]);
|
|
callWrap.call(qword[r14]);
|
|
|
|
checkMatch(R"(
|
|
lea rax,[r12+010h]
|
|
mov qword ptr [rsp+020h],rax
|
|
mov ecx,1
|
|
mov edx,2
|
|
mov r8d,3
|
|
mov r9d,4
|
|
call qword ptr [r14]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64Fixture, "ImmediateConflictWithFunction")
|
|
{
|
|
ScopedRegX64 tmp1{regs, regs.takeReg(rArg1, kInvalidInstIdx)};
|
|
ScopedRegX64 tmp2{regs, regs.takeReg(rArg2, kInvalidInstIdx)};
|
|
|
|
callWrap.addArgument(SizeX64::dword, 1);
|
|
callWrap.addArgument(SizeX64::dword, 2);
|
|
callWrap.call(qword[tmp1.release() + tmp2.release()]);
|
|
|
|
checkMatch(R"(
|
|
mov rax,rcx
|
|
mov ecx,1
|
|
mov rbx,rdx
|
|
mov edx,2
|
|
call qword ptr [rax+rbx]
|
|
)");
|
|
}
|
|
|
|
TEST_CASE_FIXTURE(IrCallWrapperX64FixtureSystemV, "SuggestedConflictWithReserved")
|
|
{
|
|
ScopedRegX64 tmp{regs, regs.takeReg(r9, kInvalidInstIdx)};
|
|
|
|
IrCallWrapperX64 callWrap(regs, build);
|
|
callWrap.addArgument(SizeX64::qword, r12);
|
|
callWrap.addArgument(SizeX64::qword, r13);
|
|
callWrap.addArgument(SizeX64::qword, r14);
|
|
callWrap.addArgument(SizeX64::dword, 2);
|
|
callWrap.addArgument(SizeX64::qword, 1);
|
|
|
|
RegisterX64 reg = callWrap.suggestNextArgumentRegister(SizeX64::dword);
|
|
build.mov(reg, 10);
|
|
callWrap.addArgument(SizeX64::dword, reg);
|
|
|
|
callWrap.call(tmp.release());
|
|
|
|
checkMatch(R"(
|
|
mov eax,Ah
|
|
mov rdi,r12
|
|
mov rsi,r13
|
|
mov rdx,r14
|
|
mov rcx,r9
|
|
mov r9d,eax
|
|
mov rax,rcx
|
|
mov ecx,2
|
|
mov r8,1
|
|
call rax
|
|
)");
|
|
}
|
|
|
|
TEST_SUITE_END();
|