#include <gtest/gtest.h>
#include "cbc/emitter/emitter.h"
#include "cbc/formater_rt.h"
#include "cbc/isa.h"
#include "cbc/isa_rt.h"
#include "mock/interpreter.h"
#include "runtimesupport/runtime.h"
#include "testutils.h"
static LimitedHeap<16384> heap;
class MemoryAccess : public testing::Test {
void SetUp() override
{
InitializeMockInterpreter();
heap.Reset();
}
void TearDown() override {}
};
using namespace Cbc::Emitter;
using namespace Cbc::Format;
using namespace Cbc;
static RTSupport::TypeInfo NewTypeInfo(size_t objectSize)
{
return RTSupport::TypeInfo(heap.New<TestTypeInfo>(objectSize));
}
TEST_F(MemoryAccess, TestAlloc)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(16);
e.Mov(IReg::IR10, IReg::IR2);
e.NewObj(ti);
e.StoreObj(Format::StoreAccessKind::ST_64, IReg::IR10, IReg::IR1, 8);
e.LoadObj(Format::LoadAccessKind::LD_64, IReg::IR1, IReg::IR1, 8);
e.Ret();
auto res = Interpret(e.Build(heap), U32(0), U64(2));
EXPECT_EQ(res.u32, 2);
}
TEST_F(MemoryAccess, TestAlloc2)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(24);
e.Mov(IReg::IR11, IReg::IR1);
e.Mov(IReg::IR12, IReg::IR2);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.StoreObj(Format::StoreAccessKind::ST_64, IReg::IR12, IReg::IR5, 8);
e.StoreObj(Format::StoreAccessKind::ST_64, IReg::IR11, IReg::IR5, 16);
e.LoadObj(Format::LoadAccessKind::LD_64, IReg::IR9, IReg::IR5, 8);
e.LoadObj(Format::LoadAccessKind::LD_64, IReg::IR10, IReg::IR5, 16);
e.Add(Width::W64, IReg::IR1, IReg::IR9, IReg::IR10);
e.Ret();
auto code = e.Build(heap);
Cbc::RT::Log(code, Stream::Disasm::rt);
auto res = Interpret(code, U64(77), U64(91));
EXPECT_EQ(res.u64, 77 + 91);
}
TEST_F(MemoryAccess, TestU8)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(16);
e.Mov(IReg::IR11, IReg::IR1);
e.Mov(IReg::IR12, IReg::IR2);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.StoreObj(Format::StoreAccessKind::ST_8, IReg::IR11, IReg::IR5, 8);
e.StoreObj(Format::StoreAccessKind::ST_8, IReg::IR12, IReg::IR5, 9);
e.LoadObj(Format::LoadAccessKind::LD_U8, IReg::IR9, IReg::IR5, 8);
e.LoadObj(Format::LoadAccessKind::LD_U8, IReg::IR10, IReg::IR5, 9);
e.Add(Width::W32, IReg::IR1, IReg::IR9, IReg::IR10);
e.Ret();
auto res = Interpret(e.Build(heap), U64(77), U64(91));
EXPECT_EQ(res.u32, 77 + 91);
}
TEST_F(MemoryAccess, TestI8)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(16);
e.Mov(IReg::IR11, IReg::IR1);
e.Mov(IReg::IR12, IReg::IR2);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.StoreObj(Format::StoreAccessKind::ST_8, IReg::IR11, IReg::IR5, 8);
e.StoreObj(Format::StoreAccessKind::ST_8, IReg::IR12, IReg::IR5, 9);
e.LoadObj(Format::LoadAccessKind::LD_S8, IReg::IR9, IReg::IR5, 8);
e.LoadObj(Format::LoadAccessKind::LD_S8, IReg::IR10, IReg::IR5, 9);
e.Add(Width::W64, IReg::IR1, IReg::IR9, IReg::IR10);
e.Ret();
auto res = Interpret(e.Build(heap), U64(128), U64(2));
auto i64 = static_cast<int64_t>(res.u64);
EXPECT_EQ(i64, -128 + 2);
}
TEST_F(MemoryAccess, TestI16)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(16);
e.Mov(IReg::IR11, IReg::IR1);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.StoreObj(Format::StoreAccessKind::ST_16, IReg::IR11, IReg::IR5, 8);
e.LoadObj(Format::LoadAccessKind::LD_S16, IReg::IR1, IReg::IR5, 8);
e.Ret();
auto res = Interpret(e.Build(heap), U64(0xffff), U64(0));
auto i64 = static_cast<int64_t>(res.u64);
EXPECT_EQ(i64, -1);
}
TEST_F(MemoryAccess, TestI32)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(16);
e.Mov(IReg::IR11, IReg::IR1);
e.Mov(IReg::IR12, IReg::IR2);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.StoreObj(Format::StoreAccessKind::ST_32, IReg::IR11, IReg::IR5, 8);
e.StoreObj(Format::StoreAccessKind::ST_32, IReg::IR12, IReg::IR5, 12);
e.LoadObj(Format::LoadAccessKind::LD_S32TO64, IReg::IR9, IReg::IR5, 8);
e.LoadObj(Format::LoadAccessKind::LD_S32TO64, IReg::IR10, IReg::IR5, 12);
e.Add(Width::W64, IReg::IR1, IReg::IR9, IReg::IR10);
e.Ret();
auto res = Interpret(e.Build(heap), U64(2147483647L + 1), U64(9));
auto i64 = static_cast<int64_t>(res.u64);
EXPECT_EQ(i64, INT32_MIN + 9);
}
TEST_F(MemoryAccess, TestU32)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(16);
e.Mov(IReg::IR11, IReg::IR1);
e.Mov(IReg::IR12, IReg::IR2);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.StoreObj(Format::StoreAccessKind::ST_32, IReg::IR11, IReg::IR5, 8);
e.StoreObj(Format::StoreAccessKind::ST_32, IReg::IR12, IReg::IR5, 12);
e.LoadObj(Format::LoadAccessKind::LD_32, IReg::IR9, IReg::IR5, 8);
e.LoadObj(Format::LoadAccessKind::LD_32, IReg::IR10, IReg::IR5, 12);
e.Add(Width::W64, IReg::IR1, IReg::IR9, IReg::IR10);
e.Ret();
auto res = Interpret(e.Build(heap), U64(2147483647L + 1), U64(9));
EXPECT_EQ(res.u64, 2147483647L + 1 + 9);
}
TEST_F(MemoryAccess, TestF32)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(16);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.FMovI32(FReg::FR0, 0.25);
e.StoreObj(Format::StoreAccessKind::ST_F32, FReg::FR0, IReg::IR5, 8);
e.LoadObj(Format::LoadAccessKind::LD_F32, FReg::FR3, IReg::IR5, 8);
e.Add(Width::W32, FReg::FR0, FReg::FR0, FReg::FR3);
e.Ret();
auto res = InterpretFPRes(e.Build(heap), U64(1), U64(2));
EXPECT_EQ(res.f32, 0.5);
}
TEST_F(MemoryAccess, TestF64)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(16);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.FMovI64(FReg::FR0, 0.25);
e.StoreObj(Format::StoreAccessKind::ST_F64, FReg::FR0, IReg::IR5, 8);
e.LoadObj(Format::LoadAccessKind::LD_F64, FReg::FR3, IReg::IR5, 8);
e.Add(Width::W64, FReg::FR0, FReg::FR0, FReg::FR3);
e.Ret();
auto res = InterpretFPRes(e.Build(heap), U64(1), U64(2));
EXPECT_EQ(res.f64, 0.5);
}
TEST_F(MemoryAccess, LinkedStack)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(24);
auto fillStack = e.NewLabel();
auto dropStack = e.NewLabel();
e.MovImm(Width::W32, IReg::IR11, 10);
e.Mov(IReg::IR10, IReg::IR11);
e.Mov(IReg::IR12, IReg::IRZ);
e.Bind(fillStack);
e.NewObj(ti);
e.StoreObj(Format::StoreAccessKind::ST_REF, IReg::IR12, IReg::IR1, 8);
e.StoreObj(Format::StoreAccessKind::ST_64, IReg::IR10, IReg::IR1, 16);
e.Mov(IReg::IR12, IReg::IR1);
e.AddI(Width::W32, IReg::IR10, IReg::IR10, static_cast<uint64_t>(-1));
e.Bcc(CC::LT, Width::W32, IReg::IRZ, IReg::IR10, fillStack);
e.Mov(IReg::IR10, IReg::IR11);
e.Mov(IReg::IR4, IReg::IRZ);
e.Bind(dropStack);
e.LoadObj(Format::LoadAccessKind::LD_64, IReg::IR3, IReg::IR12, 16);
e.Add(Width::W32, IReg::IR4, IReg::IR4, IReg::IR3);
e.LoadObj(Format::LoadAccessKind::LD_REF, IReg::IR1, IReg::IR12, 8);
e.Mov(IReg::IR12, IReg::IR1);
e.AddI(Width::W32, IReg::IR10, IReg::IR10, static_cast<uint64_t>(-2));
e.Bcc(CC::LT, Width::W32, IReg::IRZ, IReg::IR10, dropStack);
e.Mov(IReg::IR1, IReg::IR4);
e.Ret();
auto code = e.Build(heap);
Cbc::RT::Log(code, Stream::Disasm::rt);
auto res = Interpret(code, U64(77), U64(91));
EXPECT_EQ(res.u32, 1 + 2 + 3 + 4 + 5);
}
struct IntegerTest {
Format::LoadAccessKind ldk;
Format::StoreAccessKind stk;
int size;
uint64_t ir1;
uint64_t ir2;
uint64_t expect;
};
static void testInteger(IntegerTest desc)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(512);
e.MovImm(Width::W64, IReg::IR4, 2);
e.Mov(IReg::IR11, IReg::IR1);
e.Mov(IReg::IR12, IReg::IR2);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
auto mspace = e.OpenMemSpace();
mspace.Offset(31);
mspace.Offset(47);
mspace.OffsetReg(IReg::IR4);
mspace.StoreObj(desc.stk, IReg::IR2, IReg::IR5);
e.StoreObj(desc.stk, IReg::IR11, IReg::IR5, 80 + desc.size);
auto mspace2 = e.OpenMemSpace();
mspace.Offset(31);
mspace.Offset(47);
mspace.OffsetReg(IReg::IR4);
mspace.LoadObj(desc.ldk, IReg::IR11, IReg::IR5);
e.LoadObj(desc.ldk, IReg::IR12, IReg::IR5, 80 + desc.size);
e.Mov(IReg::IR3, IReg::IR11);
e.Mov(IReg::IR4, IReg::IR12);
e.Div(Width::W64, IReg::IR11, IReg::IR3, IReg::IR4);
e.Mov(IReg::IR1, IReg::IR11);
e.Mov(IReg::IR2, IReg::IR12);
e.Ret();
auto code = e.Build(heap);
Cbc::RT::Log(code, Stream::Disasm::rt);
auto res = Interpret(code, U64(desc.ir1), U64(desc.ir2));
EXPECT_EQ(res.u64, desc.expect);
}
TEST_F(MemoryAccess, SpaceS8)
{
testInteger(IntegerTest { .ldk = Format::LoadAccessKind::LD_S8,
.stk = Format::StoreAccessKind::ST_8,
.size = 1,
.ir1 = static_cast<uint64_t>(-3),
.ir2 = static_cast<uint64_t>(-9),
.expect = 3 });
}
TEST_F(MemoryAccess, SpaceS16)
{
testInteger(IntegerTest { .ldk = Format::LoadAccessKind::LD_S16,
.stk = Format::StoreAccessKind::ST_16,
.size = 2,
.ir1 = static_cast<uint64_t>(-3000),
.ir2 = static_cast<uint64_t>(-12000),
.expect = 4 });
}
TEST_F(MemoryAccess, SpaceS32)
{
testInteger(IntegerTest { .ldk = Format::LoadAccessKind::LD_S32TO64,
.stk = Format::StoreAccessKind::ST_32,
.size = 4,
.ir1 = static_cast<uint64_t>(-30000000),
.ir2 = static_cast<uint64_t>(-60000000),
.expect = 2 });
}
TEST_F(MemoryAccess, SpaceU8)
{
auto lhs = static_cast<uint64_t>(-6);
auto rhs = static_cast<uint64_t>(-3);
testInteger(IntegerTest { .ldk = Format::LoadAccessKind::LD_U8,
.stk = Format::StoreAccessKind::ST_8,
.size = 8,
.ir1 = rhs,
.ir2 = lhs,
.expect = lhs / rhs });
}
TEST_F(MemoryAccess, Space64)
{
testInteger(IntegerTest { .ldk = Format::LoadAccessKind::LD_64,
.stk = Format::StoreAccessKind::ST_64,
.size = 8,
.ir1 = static_cast<uint64_t>(-3000000000000000l),
.ir2 = static_cast<uint64_t>(-6000000000000000l),
.expect = 2 });
}
TEST_F(MemoryAccess, SpaceU32)
{
uint64_t lhs = static_cast<uint32_t>(-300000000);
auto rhs = static_cast<uint64_t>(40);
testInteger(IntegerTest { .ldk = Format::LoadAccessKind::LD_32,
.stk = Format::StoreAccessKind::ST_32,
.size = 8,
.ir1 = rhs,
.ir2 = lhs,
.expect = lhs / rhs });
}
TEST_F(MemoryAccess, Fallback)
{
Cbc::Emitter::Emitter e;
auto ti = NewTypeInfo(5000);
e.NewObj(ti);
e.Mov(IReg::IR5, IReg::IR1);
e.StoreObj(Format::StoreAccessKind::ST_64, IReg::IR2, IReg::IR5, 4096);
e.LoadObj(Format::LoadAccessKind::LD_64, IReg::IR1, IReg::IR5, 4096);
e.Ret();
auto code = e.Build(heap);
auto expected = 1231230123;
auto res = Interpret(code, U64(16), U64(expected));
EXPECT_EQ(res.u64, expected);
}
struct StructTest {
uint64_t u64;
uint32_t u32;
};
TEST_F(MemoryAccess, TestStructSpace)
{
Cbc::Emitter::Emitter e;
StructTest structTest { .u64 = 0, .u32 = 0 };
e.MovImm(Width::W64, IReg::IR4, 42);
auto mspace1 = e.OpenMemSpace();
mspace1.StoreRec(Format::StoreAccessKind::ST_64, IReg::IR4, IReg::IR2);
e.MovImm(Width::W32, IReg::IR5, 34);
auto mspace2 = e.OpenMemSpace();
mspace2.Offset(sizeof(uint64_t));
mspace2.StoreRec(Format::StoreAccessKind::ST_32, IReg::IR5, IReg::IR2);
auto mspace3 = e.OpenMemSpace();
mspace3.LoadRec(Format::LoadAccessKind::LD_64, IReg::IR6, IReg::IR2);
auto mspace4 = e.OpenMemSpace();
mspace2.Offset(sizeof(uint64_t));
mspace4.LoadRec(Format::LoadAccessKind::LD_32, IReg::IR7, IReg::IR2);
e.Add(Width::W64, IReg::IR1, IReg::IR6, IReg::IR7);
e.Ret();
auto res = Interpret(e.Build(heap), U32(0), U64(reinterpret_cast<uint64_t>(&structTest)));
EXPECT_EQ(structTest.u64, 42);
EXPECT_EQ(structTest.u32, 34);
EXPECT_EQ(res.u64, 76);
}
TEST_F(MemoryAccess, TestStruct)
{
Cbc::Emitter::Emitter e;
StructTest structTest { .u64 = 0, .u32 = 0 };
e.MovImm(Width::W64, IReg::IR4, 42);
e.StoreRec(Format::StoreAccessKind::ST_64, IReg::IR4, IReg::IR2, 0);
e.LoadRec(Format::LoadAccessKind::LD_64, IReg::IR6, IReg::IR2, 0);
e.MovImm(Width::W32, IReg::IR5, 34);
e.StoreRec(Format::StoreAccessKind::ST_32, IReg::IR5, IReg::IR2, sizeof(uint64_t));
e.LoadRec(Format::LoadAccessKind::LD_32, IReg::IR7, IReg::IR2, sizeof(uint64_t));
e.Add(Width::W64, IReg::IR1, IReg::IR6, IReg::IR7);
e.Ret();
auto res = Interpret(e.Build(heap), U32(0), U64(reinterpret_cast<uint64_t>(&structTest)));
EXPECT_EQ(structTest.u64, 42);
EXPECT_EQ(structTest.u32, 34);
EXPECT_EQ(res.u64, 76);
}
TEST_F(MemoryAccess, TestFrameSpace)
{
char frameSlots[32];
auto frameStart = reinterpret_cast<uintptr_t>(&frameSlots);
Interpretation::Frame frame { frameStart };
Cbc::Emitter::Emitter e;
e.MovImm(Width::W64, IReg::IR4, 42);
auto mspace1 = e.OpenMemSpace();
mspace1.StoreFrame(Format::StoreAccessKind::ST_64, IReg::IR4);
e.MovImm(Width::W32, IReg::IR5, 34);
auto mspace2 = e.OpenMemSpace();
mspace2.Offset(16);
mspace2.StoreFrame(Format::StoreAccessKind::ST_32, IReg::IR5);
auto mspace3 = e.OpenMemSpace();
mspace3.LoadFrame(Format::LoadAccessKind::LD_64, IReg::IR6);
auto mspace4 = e.OpenMemSpace();
mspace2.Offset(16);
mspace4.LoadFrame(Format::LoadAccessKind::LD_32, IReg::IR7);
e.Add(Width::W64, IReg::IR1, IReg::IR6, IReg::IR7);
e.Ret();
auto res = Interpret(e.Build(heap), frame, U32(0), U64(0));
EXPECT_EQ(*reinterpret_cast<long*>(frameSlots), 42);
EXPECT_EQ(*reinterpret_cast<int*>(frameSlots + 16), 34);
EXPECT_EQ(res.u64, 76);
}
TEST_F(MemoryAccess, TestFrame)
{
char frameSlots[32];
auto frameStart = reinterpret_cast<uintptr_t>(&frameSlots);
Interpretation::Frame frame { frameStart };
Cbc::Emitter::Emitter e;
e.MovImm(Width::W64, IReg::IR4, 42);
e.StoreFrame(Format::StoreAccessKind::ST_64, IReg::IR4, 0);
e.LoadFrame(Format::LoadAccessKind::LD_64, IReg::IR6, 0);
e.MovImm(Width::W32, IReg::IR5, 34);
e.StoreFrame(Format::StoreAccessKind::ST_32, IReg::IR5, 16);
e.LoadFrame(Format::LoadAccessKind::LD_32, IReg::IR7, 16);
e.Add(Width::W64, IReg::IR1, IReg::IR6, IReg::IR7);
e.Ret();
auto res = Interpret(e.Build(heap), frame, U32(0), U64(0));
EXPECT_EQ(*reinterpret_cast<long*>(frameSlots), 42);
EXPECT_EQ(*reinterpret_cast<int*>(frameSlots + 16), 34);
EXPECT_EQ(res.u64, 76);
}
TEST_F(MemoryAccess, TestFrameImm)
{
uint64_t frameSlots[10];
auto frameStart = reinterpret_cast<uintptr_t>(&frameSlots);
Interpretation::Frame frame { frameStart };
Cbc::Emitter::Emitter e;
e.StoreFrameImm(Format::StoreAccessKind::ST_8, 1, 0);
e.StoreFrameImm(Format::StoreAccessKind::ST_8, 2, 1);
e.StoreFrameImm(Format::StoreAccessKind::ST_16, 3, 2);
e.StoreFrameImm(Format::StoreAccessKind::ST_32, 4, 4);
e.StoreFrameImm(Format::StoreAccessKind::ST_64, 8, 8);
e.StoreFrameImm(Format::StoreAccessKind::ST_16, 0xfffffffffffff000, 16);
e.StoreFrameImm(Format::StoreAccessKind::ST_16, 0xfff, 18);
e.StoreFrameImm(Format::StoreAccessKind::ST_32, 0x1234, 20);
e.StoreFrameImm(Format::StoreAccessKind::ST_64, 0x4321, 24);
e.StoreFrameImm(Format::StoreAccessKind::ST_32, 0xfffffffff0000000, 32);
e.StoreFrameImm(Format::StoreAccessKind::ST_32, 0xfffffff, 36);
e.StoreFrameImm(Format::StoreAccessKind::ST_64, 0x1fffffff, 40);
e.StoreFrameImm(Format::StoreAccessKind::ST_64, 0x1fffffffffffffff, 48);
e.Ret();
auto code = e.Build(heap);
auto res = Interpret(code, frame, U32(0), U64(0));
EXPECT_EQ(
code.bytecodeSize, 13 * 4 + 5 * 2 + 4 * 3 + 3 * 5 + 1 * 9 + 1 - 3
);
EXPECT_EQ(frameSlots[0], 0x0000000400030201);
EXPECT_EQ(frameSlots[1], 0x0000000000000008);
EXPECT_EQ(frameSlots[2], 0x000012340ffff000);
EXPECT_EQ(frameSlots[3], 0x0000000000004321);
EXPECT_EQ(frameSlots[4], 0x0ffffffff0000000);
EXPECT_EQ(frameSlots[5], 0x000000001fffffff);
EXPECT_EQ(frameSlots[6], 0x1fffffffffffffff);
}