Files
asmjit/test/asmjit_test_compiler_a64.cpp
kobalicek b25df5554d [ABI] Updated instruction DB, operands, and minor API changes
This changeset contains an updated instruction database that brings
ARM32 instructions for the first time. It also updates instruction
database tooling especially for ARM64, which will also be used by
ARM32 generator.

Additionally, new operan has been added, which represents a register
list as used by ARM32 instruction set.

Other minor changes are related to ARM - some stuff had to be moved
to a64 namespace from arm namespace as it's incompatible between
32-bit and 64-bit ISA.
2023-12-26 23:28:40 +01:00

688 lines
18 KiB
C++

// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
#include <asmjit/core.h>
#if !defined(ASMJIT_NO_COMPILER) && !defined(ASMJIT_NO_AARCH64)
#include <asmjit/a64.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./asmjit_test_compiler.h"
using namespace asmjit;
// a64::Compiler - A64TestCase
// ===========================
class A64TestCase : public TestCase {
public:
A64TestCase(const char* name = nullptr)
: TestCase(name, Arch::kAArch64) {}
virtual void compile(BaseCompiler& cc) override {
compile(static_cast<a64::Compiler&>(cc));
}
virtual void compile(a64::Compiler& cc) = 0;
};
// a64::Compiler - A64Test_GpArgs
// ==============================
class A64Test_GpArgs : public A64TestCase {
public:
uint32_t _argCount;
bool _preserveFP;
A64Test_GpArgs(uint32_t argCount, bool preserveFP)
: _argCount(argCount),
_preserveFP(preserveFP) {
_name.assignFormat("GpArgs {NumArgs=%u PreserveFP=%c}", argCount, preserveFP ? 'Y' : 'N');
}
static void add(TestApp& app) {
for (uint32_t i = 0; i <= 16; i++) {
app.add(new A64Test_GpArgs(i, true));
app.add(new A64Test_GpArgs(i, false));
}
}
virtual void compile(a64::Compiler& cc) {
uint32_t i;
uint32_t argCount = _argCount;
FuncSignatureBuilder signature;
signature.setRetT<int>();
for (i = 0; i < argCount; i++)
signature.addArgT<int>();
FuncNode* funcNode = cc.addFunc(signature);
if (_preserveFP)
funcNode->frame().setPreservedFP();
a64::Gp sum;
if (argCount) {
for (i = 0; i < argCount; i++) {
a64::Gp iReg = cc.newInt32("i%u", i);
funcNode->setArg(i, iReg);
if (i == 0)
sum = iReg;
else
cc.add(sum, sum, iReg);
}
}
else {
sum = cc.newInt32("i");
cc.mov(sum, 0);
}
cc.ret(sum);
cc.endFunc();
}
virtual bool run(void* _func, String& result, String& expect) {
typedef unsigned int U;
typedef U (*Func0)();
typedef U (*Func1)(U);
typedef U (*Func2)(U, U);
typedef U (*Func3)(U, U, U);
typedef U (*Func4)(U, U, U, U);
typedef U (*Func5)(U, U, U, U, U);
typedef U (*Func6)(U, U, U, U, U, U);
typedef U (*Func7)(U, U, U, U, U, U, U);
typedef U (*Func8)(U, U, U, U, U, U, U, U);
typedef U (*Func9)(U, U, U, U, U, U, U, U, U);
typedef U (*Func10)(U, U, U, U, U, U, U, U, U, U);
typedef U (*Func11)(U, U, U, U, U, U, U, U, U, U, U);
typedef U (*Func12)(U, U, U, U, U, U, U, U, U, U, U, U);
typedef U (*Func13)(U, U, U, U, U, U, U, U, U, U, U, U, U);
typedef U (*Func14)(U, U, U, U, U, U, U, U, U, U, U, U, U, U);
typedef U (*Func15)(U, U, U, U, U, U, U, U, U, U, U, U, U, U, U);
typedef U (*Func16)(U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U);
unsigned int resultRet = 0;
unsigned int expectRet = 0;
switch (_argCount) {
case 0:
resultRet = ptr_as_func<Func0>(_func)();
expectRet = 0;
break;
case 1:
resultRet = ptr_as_func<Func1>(_func)(1);
expectRet = 1;
break;
case 2:
resultRet = ptr_as_func<Func2>(_func)(1, 2);
expectRet = 1 + 2;
break;
case 3:
resultRet = ptr_as_func<Func3>(_func)(1, 2, 3);
expectRet = 1 + 2 + 3;
break;
case 4:
resultRet = ptr_as_func<Func4>(_func)(1, 2, 3, 4);
expectRet = 1 + 2 + 3 + 4;
break;
case 5:
resultRet = ptr_as_func<Func5>(_func)(1, 2, 3, 4, 5);
expectRet = 1 + 2 + 3 + 4 + 5;
break;
case 6:
resultRet = ptr_as_func<Func6>(_func)(1, 2, 3, 4, 5, 6);
expectRet = 1 + 2 + 3 + 4 + 5 + 6;
break;
case 7:
resultRet = ptr_as_func<Func7>(_func)(1, 2, 3, 4, 5, 6, 7);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7;
break;
case 8:
resultRet = ptr_as_func<Func8>(_func)(1, 2, 3, 4, 5, 6, 7, 8);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8;
break;
case 9:
resultRet = ptr_as_func<Func9>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9;
break;
case 10:
resultRet = ptr_as_func<Func10>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10;
break;
case 11:
resultRet = ptr_as_func<Func11>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11;
break;
case 12:
resultRet = ptr_as_func<Func12>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12;
break;
case 13:
resultRet = ptr_as_func<Func13>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13;
break;
case 14:
resultRet = ptr_as_func<Func14>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14;
break;
case 15:
resultRet = ptr_as_func<Func15>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15;
break;
case 16:
resultRet = ptr_as_func<Func16>(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16;
break;
}
result.assignFormat("ret={%u, %u}", resultRet >> 28, resultRet & 0x0FFFFFFFu);
expect.assignFormat("ret={%u, %u}", expectRet >> 28, expectRet & 0x0FFFFFFFu);
return result == expect;
}
};
// a64::Compiler - A64Test_Simd1
// =============================
class A64Test_Simd1 : public A64TestCase {
public:
A64Test_Simd1()
: A64TestCase("Simd1") {}
static void add(TestApp& app) {
app.add(new A64Test_Simd1());
}
virtual void compile(a64::Compiler& cc) {
FuncNode* funcNode = cc.addFunc(FuncSignatureT<void, void*, const void*, const void*>());
a64::Gp dst = cc.newUIntPtr("dst");
a64::Gp src1 = cc.newUIntPtr("src1");
a64::Gp src2 = cc.newUIntPtr("src2");
funcNode->setArg(0, dst);
funcNode->setArg(1, src1);
funcNode->setArg(2, src2);
arm::Vec v1 = cc.newVecQ("vec1");
arm::Vec v2 = cc.newVecQ("vec2");
arm::Vec v3 = cc.newVecQ("vec3");
cc.ldr(v2, a64::ptr(src1));
cc.ldr(v3, a64::ptr(src2));
cc.add(v1.b16(), v2.b16(), v3.b16());
cc.str(v1, a64::ptr(dst));
cc.endFunc();
}
virtual bool run(void* _func, String& result, String& expect) {
typedef void (*Func)(void*, const void*, const void*);
uint32_t dst[4];
uint32_t aSrc[4] = { 0 , 1 , 2 , 255 };
uint32_t bSrc[4] = { 99, 17, 33, 1 };
// NOTE: It's a byte-add, so uint8_t(255+1) == 0.
uint32_t ref[4] = { 99, 18, 35, 0 };
ptr_as_func<Func>(_func)(dst, aSrc, bSrc);
result.assignFormat("ret={%u, %u, %u, %u}", dst[0], dst[1], dst[2], dst[3]);
expect.assignFormat("ret={%u, %u, %u, %u}", ref[0], ref[1], ref[2], ref[3]);
return result == expect;
}
};
// a64::Compiler - A64Test_ManyRegs
// ================================
class A64Test_ManyRegs : public A64TestCase {
public:
uint32_t _regCount;
A64Test_ManyRegs(uint32_t n)
: A64TestCase(),
_regCount(n) {
_name.assignFormat("GpRegs {NumRegs=%u}", n);
}
static void add(TestApp& app) {
for (uint32_t i = 2; i < 64; i++)
app.add(new A64Test_ManyRegs(i));
}
virtual void compile(a64::Compiler& cc) {
cc.addFunc(FuncSignatureT<int>());
a64::Gp* regs = static_cast<a64::Gp*>(malloc(_regCount * sizeof(a64::Gp)));
for (uint32_t i = 0; i < _regCount; i++) {
regs[i] = cc.newUInt32("reg%u", i);
cc.mov(regs[i], i + 1);
}
a64::Gp sum = cc.newUInt32("sum");
cc.mov(sum, 0);
for (uint32_t i = 0; i < _regCount; i++) {
cc.add(sum, sum, regs[i]);
}
cc.ret(sum);
cc.endFunc();
free(regs);
}
virtual bool run(void* _func, String& result, String& expect) {
typedef int (*Func)(void);
Func func = ptr_as_func<Func>(_func);
result.assignFormat("ret={%d}", func());
expect.assignFormat("ret={%d}", calcSum());
return result == expect;
}
uint32_t calcSum() const {
return (_regCount | 1) * ((_regCount + 1) / 2);
}
};
// a64::Compiler - A64Test_Adr
// ===========================
class A64Test_Adr : public A64TestCase {
public:
A64Test_Adr()
: A64TestCase("Adr") {}
static void add(TestApp& app) {
app.add(new A64Test_Adr());
}
virtual void compile(a64::Compiler& cc) {
cc.addFunc(FuncSignatureT<int>());
a64::Gp addr = cc.newIntPtr("addr");
a64::Gp val = cc.newIntPtr("val");
Label L_Table = cc.newLabel();
cc.adr(addr, L_Table);
cc.ldrsw(val, a64::ptr(addr, 8));
cc.ret(val);
cc.endFunc();
cc.bind(L_Table);
cc.embedInt32(1);
cc.embedInt32(2);
cc.embedInt32(3);
cc.embedInt32(4);
cc.embedInt32(5);
}
virtual bool run(void* _func, String& result, String& expect) {
typedef int (*Func)(void);
Func func = ptr_as_func<Func>(_func);
result.assignFormat("ret={%d}", func());
expect.assignFormat("ret={%d}", 3);
return result == expect;
}
};
// a64::Compiler - A64Test_Branch1
// ===============================
class A64Test_Branch1 : public A64TestCase {
public:
A64Test_Branch1()
: A64TestCase("Branch1") {}
static void add(TestApp& app) {
app.add(new A64Test_Branch1());
}
virtual void compile(a64::Compiler& cc) {
FuncNode* funcNode = cc.addFunc(FuncSignatureT<void, void*, size_t>());
a64::Gp p = cc.newIntPtr("p");
a64::Gp count = cc.newIntPtr("count");
a64::Gp i = cc.newIntPtr("i");
Label L = cc.newLabel();
funcNode->setArg(0, p);
funcNode->setArg(1, count);
cc.mov(i, 0);
cc.bind(L);
cc.strb(i.w(), a64::ptr(p, i));
cc.add(i, i, 1);
cc.cmp(i, count);
cc.b_ne(L);
cc.endFunc();
}
virtual bool run(void* _func, String& result, String& expect) {
typedef void (*Func)(void* p, size_t n);
Func func = ptr_as_func<Func>(_func);
uint8_t array[16];
func(array, 16);
expect.assign("ret={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}");
result.assign("ret={");
for (size_t i = 0; i < 16; i++) {
if (i)
result.append(", ");
result.appendFormat("%d", int(array[i]));
}
result.append("}");
return result == expect;
}
};
// a64::Compiler - A64Test_Invoke1
// ===============================
class A64Test_Invoke1 : public A64TestCase {
public:
A64Test_Invoke1()
: A64TestCase("Invoke1") {}
static void add(TestApp& app) {
app.add(new A64Test_Invoke1());
}
virtual void compile(a64::Compiler& cc) {
FuncNode* funcNode = cc.addFunc(FuncSignatureT<uint32_t, uint32_t, uint32_t>());
a64::Gp x = cc.newUInt32("x");
a64::Gp y = cc.newUInt32("y");
a64::Gp r = cc.newUInt32("r");
a64::Gp fn = cc.newUIntPtr("fn");
funcNode->setArg(0, x);
funcNode->setArg(1, y);
cc.mov(fn, (uint64_t)calledFunc);
InvokeNode* invokeNode;
cc.invoke(&invokeNode, fn, FuncSignatureT<uint32_t, uint32_t, uint32_t>(CallConvId::kHost));
invokeNode->setArg(0, x);
invokeNode->setArg(1, y);
invokeNode->setRet(0, r);
cc.ret(r);
cc.endFunc();
}
virtual bool run(void* _func, String& result, String& expect) {
typedef uint32_t (*Func)(uint32_t, uint32_t);
Func func = ptr_as_func<Func>(_func);
uint32_t x = 49;
uint32_t y = 7;
result.assignFormat("ret={%u}", func(x, y));
expect.assignFormat("ret={%u}", x - y);
return result == expect;
}
static uint32_t calledFunc(uint32_t x, uint32_t y) {
return x - y;
}
};
// a64::Compiler - A64Test_Invoke2
// ===============================
class A64Test_Invoke2 : public A64TestCase {
public:
A64Test_Invoke2()
: A64TestCase("Invoke2") {}
static void add(TestApp& app) {
app.add(new A64Test_Invoke2());
}
virtual void compile(a64::Compiler& cc) {
FuncNode* funcNode = cc.addFunc(FuncSignatureT<double, double, double>());
arm::Vec x = cc.newVecD("x");
arm::Vec y = cc.newVecD("y");
arm::Vec r = cc.newVecD("r");
a64::Gp fn = cc.newUIntPtr("fn");
funcNode->setArg(0, x);
funcNode->setArg(1, y);
cc.mov(fn, (uint64_t)calledFunc);
InvokeNode* invokeNode;
cc.invoke(&invokeNode, fn, FuncSignatureT<double, double, double>(CallConvId::kHost));
invokeNode->setArg(0, x);
invokeNode->setArg(1, y);
invokeNode->setRet(0, r);
cc.ret(r);
cc.endFunc();
}
virtual bool run(void* _func, String& result, String& expect) {
typedef double (*Func)(double, double);
Func func = ptr_as_func<Func>(_func);
double x = 49;
double y = 7;
result.assignFormat("ret={%f}", func(x, y));
expect.assignFormat("ret={%f}", calledFunc(x, y));
return result == expect;
}
static double calledFunc(double x, double y) {
return x - y;
}
};
// a64::Compiler - A64Test_Invoke3
// ===============================
class A64Test_Invoke3 : public A64TestCase {
public:
A64Test_Invoke3()
: A64TestCase("Invoke3") {}
static void add(TestApp& app) {
app.add(new A64Test_Invoke3());
}
virtual void compile(a64::Compiler& cc) {
FuncNode* funcNode = cc.addFunc(FuncSignatureT<double, double, double>());
arm::Vec x = cc.newVecD("x");
arm::Vec y = cc.newVecD("y");
arm::Vec r = cc.newVecD("r");
a64::Gp fn = cc.newUIntPtr("fn");
funcNode->setArg(0, x);
funcNode->setArg(1, y);
cc.mov(fn, (uint64_t)calledFunc);
InvokeNode* invokeNode;
cc.invoke(&invokeNode, fn, FuncSignatureT<double, double, double>(CallConvId::kHost));
invokeNode->setArg(0, y);
invokeNode->setArg(1, x);
invokeNode->setRet(0, r);
cc.ret(r);
cc.endFunc();
}
virtual bool run(void* _func, String& result, String& expect) {
typedef double (*Func)(double, double);
Func func = ptr_as_func<Func>(_func);
double x = 49;
double y = 7;
result.assignFormat("ret={%f}", func(x, y));
expect.assignFormat("ret={%f}", calledFunc(y, x));
return result == expect;
}
static double calledFunc(double x, double y) {
return x - y;
}
};
// a64::Compiler - A64Test_JumpTable
// =================================
class A64Test_JumpTable : public A64TestCase {
public:
bool _annotated;
A64Test_JumpTable(bool annotated)
: A64TestCase("A64Test_JumpTable"),
_annotated(annotated) {
_name.assignFormat("JumpTable {%s}", annotated ? "Annotated" : "Unknown Target");
}
enum Operator {
kOperatorAdd = 0,
kOperatorSub = 1,
kOperatorMul = 2,
kOperatorDiv = 3
};
static void add(TestApp& app) {
app.add(new A64Test_JumpTable(false));
app.add(new A64Test_JumpTable(true));
}
virtual void compile(a64::Compiler& cc) {
FuncNode* funcNode = cc.addFunc(FuncSignatureT<float, float, float, uint32_t>());
arm::Vec a = cc.newVecS("a");
arm::Vec b = cc.newVecS("b");
a64::Gp op = cc.newUInt32("op");
a64::Gp target = cc.newIntPtr("target");
a64::Gp offset = cc.newIntPtr("offset");
Label L_End = cc.newLabel();
Label L_Table = cc.newLabel();
Label L_Add = cc.newLabel();
Label L_Sub = cc.newLabel();
Label L_Mul = cc.newLabel();
Label L_Div = cc.newLabel();
funcNode->setArg(0, a);
funcNode->setArg(1, b);
funcNode->setArg(2, op);
cc.adr(target, L_Table);
cc.ldrsw(offset, a64::ptr(target, op, a64::sxtw(2)));
cc.add(target, target, offset);
// JumpAnnotation allows to annotate all possible jump targets of
// instructions where it cannot be deduced from operands.
if (_annotated) {
JumpAnnotation* annotation = cc.newJumpAnnotation();
annotation->addLabel(L_Add);
annotation->addLabel(L_Sub);
annotation->addLabel(L_Mul);
annotation->addLabel(L_Div);
cc.br(target, annotation);
}
else {
cc.br(target);
}
cc.bind(L_Add);
cc.fadd(a, a, b);
cc.b(L_End);
cc.bind(L_Sub);
cc.fsub(a, a, b);
cc.b(L_End);
cc.bind(L_Mul);
cc.fmul(a, a, b);
cc.b(L_End);
cc.bind(L_Div);
cc.fdiv(a, a, b);
cc.bind(L_End);
cc.ret(a);
cc.endFunc();
cc.bind(L_Table);
cc.embedLabelDelta(L_Add, L_Table, 4);
cc.embedLabelDelta(L_Sub, L_Table, 4);
cc.embedLabelDelta(L_Mul, L_Table, 4);
cc.embedLabelDelta(L_Div, L_Table, 4);
}
virtual bool run(void* _func, String& result, String& expect) {
typedef float (*Func)(float, float, uint32_t);
Func func = ptr_as_func<Func>(_func);
float dst[4];
float ref[4];
dst[0] = func(33.0f, 14.0f, kOperatorAdd);
dst[1] = func(33.0f, 14.0f, kOperatorSub);
dst[2] = func(10.0f, 6.0f, kOperatorMul);
dst[3] = func(80.0f, 8.0f, kOperatorDiv);
ref[0] = 47.0f;
ref[1] = 19.0f;
ref[2] = 60.0f;
ref[3] = 10.0f;
result.assignFormat("ret={%f, %f, %f, %f}", dst[0], dst[1], dst[2], dst[3]);
expect.assignFormat("ret={%f, %f, %f, %f}", ref[0], ref[1], ref[2], ref[3]);
return result == expect;
}
};
// a64::Compiler - Export
// ======================
void compiler_add_a64_tests(TestApp& app) {
app.addT<A64Test_GpArgs>();
app.addT<A64Test_ManyRegs>();
app.addT<A64Test_Simd1>();
app.addT<A64Test_Adr>();
app.addT<A64Test_Branch1>();
app.addT<A64Test_Invoke1>();
app.addT<A64Test_Invoke2>();
app.addT<A64Test_Invoke3>();
app.addT<A64Test_JumpTable>();
}
#endif // !ASMJIT_NO_COMPILER && !ASMJIT_NO_AARCH64