mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
Add RISC-V block asm support
This commit is contained in:
committed by
Christoffer Lerno
parent
d32861193b
commit
05ab0707fc
@@ -3,7 +3,7 @@ SRCS_C3 := $(wildcard *.c3)
|
||||
default: hello.elf
|
||||
|
||||
hello.a: $(SRCS_C3)
|
||||
$(C3C_PATH)c3c --use-stdlib=no --no-entry --target elf-riscv32 static-lib $(SRCS_C3)
|
||||
$(C3C_PATH)c3c -g --use-stdlib=no --no-entry --target elf-riscv32 static-lib $(SRCS_C3)
|
||||
|
||||
start.o: start.s
|
||||
riscv64-unknown-elf-as -g -march=rv32i -mabi=ilp32 -misa-spec=20191213 -o start.o start.s
|
||||
|
||||
164
src/compiler/asm/riscv.h
Normal file
164
src/compiler/asm/riscv.h
Normal file
@@ -0,0 +1,164 @@
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RISCV_X0,
|
||||
RISCV_X1,
|
||||
RISCV_X2,
|
||||
RISCV_X3,
|
||||
RISCV_X4,
|
||||
RISCV_X5,
|
||||
RISCV_X6,
|
||||
RISCV_X7,
|
||||
RISCV_X8,
|
||||
RISCV_X9,
|
||||
RISCV_X10,
|
||||
RISCV_X11,
|
||||
RISCV_X12,
|
||||
RISCV_X13,
|
||||
RISCV_X14,
|
||||
RISCV_X15,
|
||||
RISCV_X16,
|
||||
RISCV_X17,
|
||||
RISCV_X18,
|
||||
RISCV_X19,
|
||||
RISCV_X20,
|
||||
RISCV_X21,
|
||||
RISCV_X22,
|
||||
RISCV_X23,
|
||||
RISCV_X24,
|
||||
RISCV_X25,
|
||||
RISCV_X26,
|
||||
RISCV_X27,
|
||||
RISCV_X28,
|
||||
RISCV_X29,
|
||||
RISCV_X30,
|
||||
RISCV_X31,
|
||||
RISCV_F0,
|
||||
RISCV_F1,
|
||||
RISCV_F2,
|
||||
RISCV_F3,
|
||||
RISCV_F4,
|
||||
RISCV_F5,
|
||||
RISCV_F6,
|
||||
RISCV_F7,
|
||||
RISCV_F8,
|
||||
RISCV_F9,
|
||||
RISCV_F10,
|
||||
RISCV_F11,
|
||||
RISCV_F12,
|
||||
RISCV_F13,
|
||||
RISCV_F14,
|
||||
RISCV_F15,
|
||||
RISCV_F16,
|
||||
RISCV_F17,
|
||||
RISCV_F18,
|
||||
RISCV_F19,
|
||||
RISCV_F20,
|
||||
RISCV_F21,
|
||||
RISCV_F22,
|
||||
RISCV_F23,
|
||||
RISCV_F24,
|
||||
RISCV_F25,
|
||||
RISCV_F26,
|
||||
RISCV_F27,
|
||||
RISCV_F28,
|
||||
RISCV_F29,
|
||||
RISCV_F30,
|
||||
RISCV_F31,
|
||||
RISCV_MIE,
|
||||
RISCV_MSTATUS,
|
||||
RISCV_MTVEC
|
||||
} RISCVClobbers;
|
||||
|
||||
static const char *RISCVClobberNames[] = {
|
||||
[RISCV_X0] = "x0",
|
||||
[RISCV_X1] = "x1",
|
||||
[RISCV_X2] = "x2",
|
||||
[RISCV_X3] = "x3",
|
||||
[RISCV_X4] = "x4",
|
||||
[RISCV_X5] = "x5",
|
||||
[RISCV_X6] = "x6",
|
||||
[RISCV_X7] = "x7",
|
||||
[RISCV_X8] = "x8",
|
||||
[RISCV_X9] = "x9",
|
||||
[RISCV_X10] = "x10",
|
||||
[RISCV_X11] = "x11",
|
||||
[RISCV_X12] = "x12",
|
||||
[RISCV_X13] = "x13",
|
||||
[RISCV_X14] = "x14",
|
||||
[RISCV_X15] = "x15",
|
||||
[RISCV_X16] = "x16",
|
||||
[RISCV_X17] = "x17",
|
||||
[RISCV_X18] = "x18",
|
||||
[RISCV_X19] = "x19",
|
||||
[RISCV_X20] = "x20",
|
||||
[RISCV_X21] = "x21",
|
||||
[RISCV_X22] = "x22",
|
||||
[RISCV_X23] = "x23",
|
||||
[RISCV_X24] = "x24",
|
||||
[RISCV_X25] = "x25",
|
||||
[RISCV_X26] = "x26",
|
||||
[RISCV_X27] = "x27",
|
||||
[RISCV_X28] = "x28",
|
||||
[RISCV_X29] = "x29",
|
||||
[RISCV_X30] = "x30",
|
||||
[RISCV_X31] = "x31",
|
||||
[RISCV_F0] = "f0",
|
||||
[RISCV_F1] = "f1",
|
||||
[RISCV_F2] = "f2",
|
||||
[RISCV_F3] = "f3",
|
||||
[RISCV_F4] = "f4",
|
||||
[RISCV_F5] = "f5",
|
||||
[RISCV_F6] = "f6",
|
||||
[RISCV_F7] = "f7",
|
||||
[RISCV_F8] = "f8",
|
||||
[RISCV_F9] = "f9",
|
||||
[RISCV_F10] = "f10",
|
||||
[RISCV_F11] = "f11",
|
||||
[RISCV_F12] = "f12",
|
||||
[RISCV_F13] = "f13",
|
||||
[RISCV_F14] = "f14",
|
||||
[RISCV_F15] = "f15",
|
||||
[RISCV_F16] = "f16",
|
||||
[RISCV_F17] = "f17",
|
||||
[RISCV_F18] = "f18",
|
||||
[RISCV_F19] = "f19",
|
||||
[RISCV_F20] = "f20",
|
||||
[RISCV_F21] = "f21",
|
||||
[RISCV_F22] = "f22",
|
||||
[RISCV_F23] = "f23",
|
||||
[RISCV_F24] = "f24",
|
||||
[RISCV_F25] = "f25",
|
||||
[RISCV_F26] = "f26",
|
||||
[RISCV_F27] = "f27",
|
||||
[RISCV_F28] = "f28",
|
||||
[RISCV_F29] = "f29",
|
||||
[RISCV_F30] = "f30",
|
||||
[RISCV_F31] = "f31",
|
||||
[RISCV_MIE] = "mie",
|
||||
[RISCV_MSTATUS] = "mstatus",
|
||||
[RISCV_MTVEC] = "mtvec",
|
||||
};
|
||||
|
||||
static const char *riscv_gp_integer_regs[] = { "$x0", "$x1", "$x2", "$x3", "$x4",
|
||||
"$x5", "$x6", "$x7", "$x8", "$x9",
|
||||
"$x10", "$x11", "$x12", "$x13", "$x14",
|
||||
"$x15", "$x16", "$x17", "$x18", "$x19",
|
||||
"$x20", "$x21", "$x22", "$x23", "$x24",
|
||||
"$x25", "$x26", "$x27", "$x28", "$x29",
|
||||
"$x30", "$x31" };
|
||||
static const char *riscv_arg_integer_regs[] = { "$a0", "$a1", "$a2", "$a3", "$a4",
|
||||
"$a5", "$a6", "$a7" };
|
||||
static const char *riscv_temp_integer_regs[] = { "$t0", "$t1", "$t2", "$t3", "$t4",
|
||||
"$t5", "$t6" };
|
||||
static const char *riscv_save_integer_regs[] = { "$s0", "$s1", "$s2", "$s3", "$s4",
|
||||
"$s5", "$s6", "$s7", "$s8", "$s9", "$s10", "$s11" };
|
||||
static const char *riscv_float_regs[] = { "$f0", "$f1", "$f2", "$f3", "$f4",
|
||||
"$f5", "$f6", "$f7", "$f8", "$f9",
|
||||
"$f10", "$f11", "$f12", "$f13", "$f14",
|
||||
"$f15", "$f16", "$f17", "$f18", "$f19",
|
||||
"$f20", "$f21", "$f22", "$f23", "$f24",
|
||||
"$f25", "$f26", "$f27", "$f28", "$f29",
|
||||
"$f30", "$f31" };
|
||||
static const char *riscv_machine_integer_regs[] = { "$mie", "$mstatus", "$mtvec" };
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "compiler_internal.h"
|
||||
#include "compiler/asm/x86.h"
|
||||
#include "compiler/asm/aarch64.h"
|
||||
#include "compiler/asm/riscv.h"
|
||||
|
||||
#define ASM_PTR_HASH(name__) (uint32_t)(((uintptr_t)name__ * 31) ^ ((uintptr_t)name__ >> 15))
|
||||
|
||||
@@ -46,6 +47,11 @@ INLINE AsmArgBits parse_bits(const char **desc)
|
||||
*desc += 2;
|
||||
return ARG_BITS_16;
|
||||
}
|
||||
if (memcmp("20", *desc, 2) == 0)
|
||||
{
|
||||
*desc += 2;
|
||||
return ARG_BITS_20;
|
||||
}
|
||||
if (memcmp("32", *desc, 2) == 0)
|
||||
{
|
||||
*desc += 2;
|
||||
@@ -71,6 +77,16 @@ INLINE AsmArgBits parse_bits(const char **desc)
|
||||
*desc += 3;
|
||||
return ARG_BITS_512;
|
||||
}
|
||||
if (memcmp("5", *desc, 1) == 0)
|
||||
{
|
||||
*desc += 1;
|
||||
return ARG_BITS_5;
|
||||
}
|
||||
if (memcmp("12", *desc, 2) == 0)
|
||||
{
|
||||
*desc += 2;
|
||||
return ARG_BITS_12;
|
||||
}
|
||||
error_exit("Invalid bits: %s.", *desc);
|
||||
}
|
||||
|
||||
@@ -122,7 +138,7 @@ INLINE AsmArgType decode_arg_type(const char **desc)
|
||||
}
|
||||
if (c == 'u')
|
||||
{
|
||||
desc++;
|
||||
(*desc)++;
|
||||
arg_type.imm_arg_ubits |= parse_bits(desc);
|
||||
goto NEXT;
|
||||
}
|
||||
@@ -279,7 +295,165 @@ static void init_asm_arm(PlatformTarget *target)
|
||||
|
||||
static void init_asm_riscv(PlatformTarget *target)
|
||||
{
|
||||
error_exit("RISCV asm not complete.");
|
||||
target->clobber_name_list = RISCVClobberNames;
|
||||
target->extra_clobbers = NULL;
|
||||
unsigned int bits = 0;
|
||||
switch(target->arch) {
|
||||
case ARCH_TYPE_RISCV64:
|
||||
// math
|
||||
reg_instr(target, "add", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "sub", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "addi", "w:r64/mem, r64/mem, immi12");
|
||||
reg_instr(target, "neg", "w:r64/mem, r64/mem");
|
||||
// bit
|
||||
reg_instr(target, "and", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "or", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "xor", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "not", "w:r64/mem, r64/mem");
|
||||
reg_instr(target, "andi", "w:r64/mem, r64/mem, immi12");
|
||||
reg_instr(target, "ori", "w:r64/mem, r64/mem, immi12");
|
||||
reg_instr(target, "xori", "w:r64/mem, r64/mem, immi12");
|
||||
// shift
|
||||
reg_instr(target, "sll", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "srl", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "sra", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "slli", "w:r64/mem, r64/mem, immu5");
|
||||
reg_instr(target, "srli", "w:r64/mem, r64/mem, immu5");
|
||||
reg_instr(target, "srai", "w:r64/mem, r64/mem, immu5");
|
||||
// load
|
||||
reg_instr(target, "li", "w:r64/mem, immi64");
|
||||
reg_instr(target, "lui", "w:r64, immu20");
|
||||
reg_instr(target, "auipc", "w:r64, immi20");
|
||||
reg_instr(target, "mv", "w:r64/mem, r64/mem");
|
||||
reg_instr(target, "ld", "w:r64/mem, mem");
|
||||
reg_instr(target, "lw", "w:r64/mem, mem");
|
||||
reg_instr(target, "lh", "w:r64/mem, mem");
|
||||
reg_instr(target, "lhu", "w:r64/mem, mem");
|
||||
reg_instr(target, "lb", "w:r64/mem, mem");
|
||||
reg_instr(target, "lbu", "w:r64/mem, mem");
|
||||
// store
|
||||
reg_instr(target, "sd", "r64/mem, w:mem");
|
||||
reg_instr(target, "sw", "r64/mem, w:mem");
|
||||
reg_instr(target, "sh", "r64/mem, w:mem");
|
||||
reg_instr(target, "sb", "r64/mem, w:mem");
|
||||
// Misc
|
||||
reg_instr(target, "nop", NULL);
|
||||
reg_instr(target, "ebreak", NULL);
|
||||
reg_instr(target, "ecall", NULL);
|
||||
reg_instr(target, "eret", NULL);
|
||||
// Jump
|
||||
reg_instr(target, "j", "immi20");
|
||||
reg_instr(target, "jal", "w:r64/mem, immi20");
|
||||
reg_instr(target, "jalr", "w:r64/mem, r64/mem, immi20");
|
||||
reg_instr(target, "ret", NULL);
|
||||
// Set
|
||||
reg_instr(target, "slt", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "slti", "w:r64/mem, r64/mem, immi12");
|
||||
reg_instr(target, "sltu", "w:r64/mem, r64/mem, r64/mem");
|
||||
reg_instr(target, "sltiu", "w:r64/mem, r64/mem, immu12");
|
||||
reg_instr(target, "seqz", "w:r64/mem, r64/mem");
|
||||
reg_instr(target, "snez", "w:r64/mem, r64/mem");
|
||||
reg_instr(target, "sltz", "w:r64/mem, r64/mem");
|
||||
reg_instr(target, "sgtz", "w:r64/mem, r64/mem");
|
||||
// CSR
|
||||
reg_instr(target, "csrw", "w:r64, r64/mem");
|
||||
reg_instr(target, "csrr", "w:r64/mem, r64");
|
||||
reg_instr(target, "csrrw", "w:r64/mem, rw:r64, r64/mem");
|
||||
reg_instr(target, "csrrs", "w:r64/mem, rw:r64, r64/mem");
|
||||
reg_instr(target, "csrrc", "w:r64/mem, rw:r64, r64/mem");
|
||||
reg_instr(target, "csrrwi", "w:r64/mem, rw:r64, immu5");
|
||||
reg_instr(target, "csrrsi", "w:r64/mem, rw:r64, immu5");
|
||||
reg_instr(target, "csrrci", "w:r64/mem, rw:r64, immu5");
|
||||
// Interrupt
|
||||
reg_instr(target, "wfi", NULL);
|
||||
reg_instr(target, "mret", NULL);
|
||||
|
||||
bits = ARG_BITS_64;
|
||||
break;
|
||||
case ARCH_TYPE_RISCV32:
|
||||
// math
|
||||
reg_instr(target, "add", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "sub", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "addi", "w:r32/mem, r32/mem, immi12");
|
||||
reg_instr(target, "neg", "w:r32/mem, r32/mem");
|
||||
// bit
|
||||
reg_instr(target, "and", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "or", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "xor", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "not", "w:r32/mem, r32/mem");
|
||||
reg_instr(target, "andi", "w:r32/mem, r32/mem, immi12");
|
||||
reg_instr(target, "ori", "w:r32/mem, r32/mem, immi12");
|
||||
reg_instr(target, "xori", "w:r32/mem, r32/mem, immi12");
|
||||
// shift
|
||||
reg_instr(target, "sll", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "srl", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "sra", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "slli", "w:r32/mem, r32/mem, immu5");
|
||||
reg_instr(target, "srli", "w:r32/mem, r32/mem, immu5");
|
||||
reg_instr(target, "srai", "w:r32/mem, r32/mem, immu5");
|
||||
// load
|
||||
reg_instr(target, "li", "w:r32/mem, immi32");
|
||||
reg_instr(target, "lui", "w:r32, immu20");
|
||||
reg_instr(target, "auipc", "w:r32, immi20");
|
||||
reg_instr(target, "mv", "w:r32/mem, r32/mem");
|
||||
reg_instr(target, "lw", "w:r32/mem, mem");
|
||||
reg_instr(target, "lh", "w:r32/mem, mem");
|
||||
reg_instr(target, "lhu", "w:r32/mem, mem");
|
||||
reg_instr(target, "lb", "w:r32/mem, mem");
|
||||
reg_instr(target, "lbu", "w:r32/mem, mem");
|
||||
// store
|
||||
reg_instr(target, "sw", "r32/mem, w:mem");
|
||||
reg_instr(target, "sh", "r32/mem, w:mem");
|
||||
reg_instr(target, "sb", "r32/mem, w:mem");
|
||||
// Misc
|
||||
reg_instr(target, "nop", NULL);
|
||||
reg_instr(target, "ebreak", NULL);
|
||||
reg_instr(target, "ecall", NULL);
|
||||
reg_instr(target, "eret", NULL);
|
||||
// Jump
|
||||
reg_instr(target, "j", "immi20");
|
||||
reg_instr(target, "jal", "w:r32/mem, immi20");
|
||||
reg_instr(target, "jalr", "w:r32/mem, r32/mem, immi20");
|
||||
reg_instr(target, "ret", NULL);
|
||||
// Set
|
||||
reg_instr(target, "slt", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "slti", "w:r32/mem, r32/mem, immi12");
|
||||
reg_instr(target, "sltu", "w:r32/mem, r32/mem, r32/mem");
|
||||
reg_instr(target, "sltiu", "w:r32/mem, r32/mem, immu12");
|
||||
reg_instr(target, "seqz", "w:r32/mem, r32/mem");
|
||||
reg_instr(target, "snez", "w:r32/mem, r32/mem");
|
||||
reg_instr(target, "sltz", "w:r32/mem, r32/mem");
|
||||
reg_instr(target, "sgtz", "w:r32/mem, r32/mem");
|
||||
// CSR
|
||||
reg_instr(target, "csrw", "w:r32, r32/mem");
|
||||
reg_instr(target, "csrr", "w:r32/mem, r32");
|
||||
reg_instr(target, "csrrw", "w:r32/mem, rw:r32, r32/mem");
|
||||
reg_instr(target, "csrrs", "w:r32/mem, rw:r32, r32/mem");
|
||||
reg_instr(target, "csrrc", "w:r32/mem, rw:r32, r32/mem");
|
||||
reg_instr(target, "csrrwi", "w:r32/mem, rw:r32, immu5");
|
||||
reg_instr(target, "csrrsi", "w:r32/mem, rw:r32, immu5");
|
||||
reg_instr(target, "csrrci", "w:r32/mem, rw:r32, immu5");
|
||||
// Interrupt
|
||||
reg_instr(target, "wfi", NULL);
|
||||
reg_instr(target, "mret", NULL);
|
||||
|
||||
bits = ARG_BITS_32;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
reg_register_list(target, riscv_gp_integer_regs, 32, ASM_REG_INT, bits, RISCV_X0);
|
||||
reg_register_list(target, riscv_arg_integer_regs, 8, ASM_REG_INT, bits, RISCV_X10);
|
||||
reg_register_list(target, riscv_temp_integer_regs, 3, ASM_REG_INT, bits, RISCV_X5);
|
||||
reg_register_list(target, &riscv_temp_integer_regs[3], 4, ASM_REG_INT, bits, RISCV_X28);
|
||||
reg_register_list(target, riscv_save_integer_regs, 2, ASM_REG_INT, bits, RISCV_X8);
|
||||
reg_register_list(target, &riscv_save_integer_regs[2], 10, ASM_REG_INT, bits, RISCV_X18);
|
||||
reg_register_list(target, riscv_machine_integer_regs, 3, ASM_REG_INT, bits, RISCV_MIE);
|
||||
reg_register(target, "$ra", ASM_REG_INT, bits, RISCV_X1);
|
||||
reg_register(target, "$sp", ASM_REG_INT, bits, RISCV_X2);
|
||||
reg_register(target, "$gp", ASM_REG_INT, bits, RISCV_X3);
|
||||
reg_register(target, "$tp", ASM_REG_INT, bits, RISCV_X4);
|
||||
reg_register(target, "$zero", ASM_REG_INT, bits, RISCV_X0);
|
||||
}
|
||||
|
||||
static void init_asm_ppc(PlatformTarget *target)
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
#include "codegen_internal.h"
|
||||
|
||||
// Macro to cast an unsigned value `x` to a signed integer of `n` bits, then extend to int64_t
|
||||
#define CAST_AND_EXTEND(x, n) \
|
||||
(((int64_t)((x) << (64 - (n)))) >> (64 - (n)))
|
||||
|
||||
static inline void codegen_create_x86att_arg(AsmInlineBlock *block, unsigned input_offset, Expr *expr)
|
||||
{
|
||||
ExprAsmArg *arg = &expr->expr_asm_arg;
|
||||
@@ -112,6 +116,57 @@ static inline void codegen_create_aarch64_arg(AsmInlineBlock *block, unsigned in
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static inline void codegen_create_riscv_arg(AsmInlineBlock *block, unsigned input_offset, Expr *expr)
|
||||
{
|
||||
ExprAsmArg *arg = &expr->expr_asm_arg;
|
||||
switch (arg->kind)
|
||||
{
|
||||
case ASM_ARG_INT:
|
||||
if (arg->is_neg)
|
||||
{
|
||||
scratch_buffer_append_signed_int(CAST_AND_EXTEND(arg->value, arg->bits));
|
||||
}
|
||||
else
|
||||
{
|
||||
scratch_buffer_append_unsigned_int(arg->value);
|
||||
}
|
||||
return;
|
||||
case ASM_ARG_REG:
|
||||
scratch_buffer_append(&arg->reg.ref->name[1]);
|
||||
return;
|
||||
case ASM_ARG_VALUE:
|
||||
scratch_buffer_append_char('$');
|
||||
scratch_buffer_append_unsigned_int(arg->index + input_offset);
|
||||
return;
|
||||
case ASM_ARG_MEMVAR:
|
||||
case ASM_ARG_REGVAR:
|
||||
scratch_buffer_append_char('$');
|
||||
if (arg->ident.is_input && !arg->ident.copy_output)
|
||||
{
|
||||
scratch_buffer_append_unsigned_int(arg->index + input_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
scratch_buffer_append_unsigned_int(arg->index);
|
||||
}
|
||||
return;
|
||||
case ASM_ARG_ADDR:
|
||||
if (arg->idx || arg->offset_type) TODO;
|
||||
if (arg->neg_offset) scratch_buffer_append_char('-');
|
||||
scratch_buffer_append_unsigned_int(arg->offset);
|
||||
scratch_buffer_append_char('(');
|
||||
if (arg->base)
|
||||
{
|
||||
codegen_create_riscv_arg(block, input_offset, exprptr(arg->base));
|
||||
}
|
||||
scratch_buffer_append_char(')');
|
||||
return;
|
||||
case ASM_ARG_ADDROF:
|
||||
TODO
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
static inline char *codegen_create_x86_att_asm(AsmInlineBlock *block)
|
||||
{
|
||||
@@ -161,6 +216,30 @@ static inline char *codegen_create_aarch64_asm(AsmInlineBlock *block)
|
||||
return scratch_buffer_to_string();
|
||||
}
|
||||
|
||||
static inline char *codegen_create_riscv_asm(AsmInlineBlock *block)
|
||||
{
|
||||
AstId next = block->asm_stmt;
|
||||
scratch_buffer_clear();
|
||||
unsigned input_arg_offset = vec_size(block->output_vars);
|
||||
while (next)
|
||||
{
|
||||
Ast *ast = astptr(next);
|
||||
next = ast->next;
|
||||
scratch_buffer_append(ast->asm_stmt.instruction);
|
||||
Expr** args = ast->asm_stmt.args;
|
||||
unsigned arg_count = vec_size(args);
|
||||
scratch_buffer_append_char(' ');
|
||||
for (unsigned i = 0; i < arg_count; i++)
|
||||
{
|
||||
if (i > 0) scratch_buffer_append(", ");
|
||||
codegen_create_riscv_arg(block, input_arg_offset, args[i]);
|
||||
}
|
||||
scratch_buffer_append_char('\n');
|
||||
}
|
||||
|
||||
return scratch_buffer_to_string();
|
||||
}
|
||||
|
||||
const char *codegen_create_asm(Ast *ast)
|
||||
{
|
||||
assert(ast->ast_kind == AST_ASM_BLOCK_STMT);
|
||||
@@ -174,5 +253,9 @@ const char *codegen_create_asm(Ast *ast)
|
||||
{
|
||||
return codegen_create_aarch64_asm(block);
|
||||
}
|
||||
if (compiler.platform.arch == ARCH_TYPE_RISCV32 || compiler.platform.arch == ARCH_TYPE_RISCV64)
|
||||
{
|
||||
return codegen_create_riscv_asm(block);
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -24,6 +24,16 @@ typedef uint32_t ArraySize;
|
||||
typedef uint64_t BitSize;
|
||||
typedef uint16_t FileId;
|
||||
|
||||
#define INT5_MAX 15
|
||||
#define INT12_MAX 2047
|
||||
#define INT20_MAX 524287
|
||||
#define INT5_MIN -16
|
||||
#define INT12_MIN -2048
|
||||
#define INT20_MIN (-INT20_MAX-1)
|
||||
#define UINT5_MAX 31
|
||||
#define UINT12_MAX 4095
|
||||
#define UINT20_MAX 1048575U
|
||||
|
||||
#define MAX_ARRAYINDEX INT32_MAX
|
||||
#define MAX_FIXUPS 0xFFFFF
|
||||
#define MAX_HASH_SIZE (512 * 1024 * 1024)
|
||||
@@ -874,7 +884,11 @@ typedef struct
|
||||
ExprId idx;
|
||||
uint64_t offset;
|
||||
};
|
||||
uint64_t value;
|
||||
struct {
|
||||
uint64_t value;
|
||||
unsigned bits;
|
||||
bool is_neg;
|
||||
};
|
||||
union
|
||||
{
|
||||
const char *name;
|
||||
@@ -3583,8 +3597,11 @@ INLINE unsigned arg_bits_max(AsmArgBits bits, unsigned limit)
|
||||
if (limit >= 80 && (bits & ARG_BITS_80)) return 80;
|
||||
if (limit >= 64 && (bits & ARG_BITS_64)) return 64;
|
||||
if (limit >= 32 && (bits & ARG_BITS_32)) return 32;
|
||||
if (limit >= 20 && (bits & ARG_BITS_20)) return 20;
|
||||
if (limit >= 16 && (bits & ARG_BITS_16)) return 16;
|
||||
if (limit >= 12 && (bits & ARG_BITS_12)) return 12;
|
||||
if (limit >= 8 && (bits & ARG_BITS_8)) return 8;
|
||||
if (limit >= 5 && (bits & ARG_BITS_5)) return 5;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3674,3 +3691,4 @@ INLINE bool check_module_name(Path *path)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -142,6 +142,9 @@ typedef enum FLAG_ATTR
|
||||
ARG_BITS_256 = 1 << 5,
|
||||
ARG_BITS_512 = 1 << 6,
|
||||
ARG_BITS_80 = 1 << 7,
|
||||
ARG_BITS_5 = 1 << 8,
|
||||
ARG_BITS_12 = 1 << 9,
|
||||
ARG_BITS_20 = 1 << 10,
|
||||
} AsmArgBits;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -330,6 +330,10 @@ static inline Expr *parse_asm_expr(ParseContext *c)
|
||||
return poisoned_expr;
|
||||
}
|
||||
return expr;
|
||||
case TOKEN_MINUS:
|
||||
expr->expr_asm_arg.kind = ASM_ARG_VALUE;
|
||||
ASSIGN_EXPRID_OR_RET(expr->expr_asm_arg.expr_id, parse_expr(c), poisoned_expr);
|
||||
return expr;
|
||||
case TOKEN_INTEGER:
|
||||
case TOKEN_CONST_IDENT:
|
||||
case TOKEN_REAL:
|
||||
|
||||
@@ -22,11 +22,11 @@ static inline Type *max_supported_imm_int(bool is_signed, AsmArgType arg)
|
||||
{
|
||||
unsigned bits = arg_bits_max(arg.imm_arg_ibits, 64);
|
||||
if (!bits) return NULL;
|
||||
return type_int_signed_by_bitsize(bits);
|
||||
return type_int_signed_by_bitsize(next_highest_power_of_2(bits));
|
||||
}
|
||||
unsigned bits = arg_bits_max(arg.imm_arg_ubits, 64);
|
||||
if (!bits) return NULL;
|
||||
return type_int_unsigned_by_bitsize(bits);
|
||||
return type_int_unsigned_by_bitsize(next_highest_power_of_2(bits));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -53,7 +53,7 @@ static inline bool sema_reg_int_suported_type(AsmArgType arg, Type *type)
|
||||
{
|
||||
assert(type_flatten(type) == type);
|
||||
unsigned bits = type_bit_size(type);
|
||||
return arg_bits_max(arg.ireg_bits, bits) == bits;
|
||||
return next_highest_power_of_2(arg_bits_max(arg.ireg_bits, bits)) == bits;
|
||||
}
|
||||
|
||||
INLINE bool sema_reg_is_valid_in_slot(AsmRegister *reg, AsmArgType arg_type)
|
||||
@@ -75,7 +75,91 @@ static inline bool sema_reg_float_suported_type(AsmArgType arg, Type *type)
|
||||
{
|
||||
assert(type_flatten(type) == type);
|
||||
if (!arg.float_bits) return false;
|
||||
return type_bit_size(type) == arg_bits_max(arg.float_bits, 0);
|
||||
return type_bit_size(type) == next_highest_power_of_2(arg_bits_max(arg.float_bits, 0));
|
||||
}
|
||||
|
||||
static inline bool sema_check_imm_fits(Int imm, AsmArgType arg_type)
|
||||
{
|
||||
Int128 min = {0};
|
||||
Int128 max = {0};
|
||||
bool is_signed = false;
|
||||
// Check if actually an immediate. If not, just move along.
|
||||
if (arg_type.imm_arg_ibits == 0 && arg_type.imm_arg_ubits == 0) return true;
|
||||
if (arg_type.imm_arg_ibits > 0)
|
||||
{
|
||||
if (arg_type.imm_arg_ibits & ARG_BITS_20)
|
||||
{
|
||||
min = i128_from_signed(INT20_MIN);
|
||||
max = i128_from_signed(INT20_MAX);
|
||||
is_signed = true;
|
||||
}
|
||||
else if (arg_type.imm_arg_ibits & ARG_BITS_12)
|
||||
{
|
||||
min = i128_from_signed(INT12_MIN);
|
||||
max = i128_from_signed(INT12_MAX);
|
||||
is_signed = true;
|
||||
}
|
||||
else if (arg_type.imm_arg_ibits & ARG_BITS_5)
|
||||
{
|
||||
min = i128_from_signed(INT5_MIN);
|
||||
max = i128_from_signed(INT5_MAX);
|
||||
is_signed = true;
|
||||
}
|
||||
else if (arg_type.imm_arg_ibits & (ARG_BITS_8|ARG_BITS_16|ARG_BITS_32|ARG_BITS_64|ARG_BITS_128|ARG_BITS_256|ARG_BITS_512|ARG_BITS_80))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (arg_type.imm_arg_ubits > 0)
|
||||
{
|
||||
if (arg_type.imm_arg_ubits & ARG_BITS_20)
|
||||
{
|
||||
max = (Int128){ 0, UINT20_MAX };
|
||||
}
|
||||
else if (arg_type.imm_arg_ubits & ARG_BITS_12)
|
||||
{
|
||||
max = (Int128){ 0, UINT12_MAX };
|
||||
}
|
||||
else if (arg_type.imm_arg_ubits & ARG_BITS_5)
|
||||
{
|
||||
max = (Int128){ 0, UINT5_MAX };
|
||||
}
|
||||
else if (arg_type.imm_arg_ubits & (ARG_BITS_8|ARG_BITS_16|ARG_BITS_32|ARG_BITS_64|ARG_BITS_128|ARG_BITS_256|ARG_BITS_512|ARG_BITS_80))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Not an immediate of these off-brand types, so just move along.
|
||||
if (i128_is_zero(min) && i128_is_zero(max)) return true;
|
||||
bool op_is_signed = type_kind_is_signed(imm.type);
|
||||
if (is_signed)
|
||||
{
|
||||
if (op_is_signed)
|
||||
{
|
||||
if (i128_scomp(imm.i, min) == CMP_LT) return false;
|
||||
if (i128_scomp(imm.i, max) == CMP_GT) return false;
|
||||
return true;
|
||||
}
|
||||
// In the unsigned case, we don't need to test the lower limit.
|
||||
return i128_ucomp(imm.i, max) != CMP_GT;
|
||||
}
|
||||
if (op_is_signed)
|
||||
{
|
||||
if (i128_is_neg(imm.i)) return false;
|
||||
if (i128_ucomp(imm.i, max) == CMP_GT) return false;
|
||||
return true;
|
||||
}
|
||||
// In the unsigned case, we don't need to test the lower limit.
|
||||
return i128_ucomp(imm.i, max) != CMP_GT;
|
||||
}
|
||||
|
||||
static inline bool sema_check_asm_arg_const_int(SemaContext *context, AsmInlineBlock *block, AsmInstruction *instr, AsmArgType arg_type, Expr *expr, Expr *int_expr)
|
||||
@@ -88,14 +172,18 @@ static inline bool sema_check_asm_arg_const_int(SemaContext *context, AsmInlineB
|
||||
return false;
|
||||
}
|
||||
Int i = int_expr->const_expr.ixx;
|
||||
if (!type || !int_fits(i, type->type_kind))
|
||||
unsigned max_bits = arg_bits_max(is_signed ? arg_type.imm_arg_ibits : arg_type.imm_arg_ubits, 0);
|
||||
if (!type || !int_fits(i, type->type_kind) || !sema_check_imm_fits(i, arg_type))
|
||||
{
|
||||
SEMA_ERROR(expr, "'%s' expected %s.", instr->name, type_quoted_error_string(type));
|
||||
SEMA_ERROR(expr, "'%s' expected %s limited to %d bits.", instr->name, type_quoted_error_string(type), max_bits);
|
||||
return false;
|
||||
}
|
||||
// Because we assume max 64 bit imm, we can do this simple cast for signed values.
|
||||
expr->expr_asm_arg.is_neg = false;
|
||||
expr->expr_asm_arg.bits = max_bits;
|
||||
if (is_signed)
|
||||
{
|
||||
expr->expr_asm_arg.is_neg = i128_is_neg(int_expr->const_expr.ixx.i);
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_I8:
|
||||
@@ -175,6 +263,18 @@ static inline bool sema_check_asm_arg_addr(SemaContext *context, AsmInlineBlock
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((compiler.platform.arch == ARCH_TYPE_RISCV32 ||
|
||||
compiler.platform.arch == ARCH_TYPE_RISCV64) &&
|
||||
asm_arg->offset)
|
||||
{
|
||||
if ((asm_arg->neg_offset && asm_arg->offset > abs(INT12_MIN)) ||
|
||||
(!asm_arg->neg_offset && asm_arg->offset > INT12_MAX))
|
||||
{
|
||||
SEMA_ERROR(expr, "RISC-V offset limited to 12-bits signed.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
REMINDER("check if addressing mode is supported");
|
||||
return true;
|
||||
}
|
||||
@@ -477,7 +577,10 @@ static inline bool sema_check_asm_arg(SemaContext *context, AsmInlineBlock *bloc
|
||||
}
|
||||
bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt)
|
||||
{
|
||||
if (compiler.platform.arch != ARCH_TYPE_X86_64 && compiler.platform.arch != ARCH_TYPE_AARCH64)
|
||||
if (compiler.platform.arch != ARCH_TYPE_X86_64 &&
|
||||
compiler.platform.arch != ARCH_TYPE_AARCH64 &&
|
||||
compiler.platform.arch != ARCH_TYPE_RISCV32 &&
|
||||
compiler.platform.arch != ARCH_TYPE_RISCV64)
|
||||
{
|
||||
SEMA_ERROR(asm_stmt, "Unsupported architecture for asm.");
|
||||
return false;
|
||||
|
||||
@@ -7,6 +7,16 @@
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
#define INT5_MAX 15
|
||||
#define INT12_MAX 2047
|
||||
#define INT20_MAX 524287
|
||||
#define INT5_MIN -16
|
||||
#define INT12_MIN -2048
|
||||
#define INT20_MIN (-INT20_MAX-1)
|
||||
#define UINT5_MAX 31
|
||||
#define UINT12_MAX 4095
|
||||
#define UINT20_MAX 1048575U
|
||||
|
||||
#define SEMA_ERROR(_node, ...) sema_error_at(context, (_node)->span, __VA_ARGS__)
|
||||
#define RETURN_SEMA_ERROR(_node, ...) do { sema_error_at(context, (_node)->span, __VA_ARGS__); return false; } while (0)
|
||||
|
||||
|
||||
20
test/test_suite/asm/asm_bit_rv.c3t
Normal file
20
test/test_suite/asm/asm_bit_rv.c3t
Normal file
@@ -0,0 +1,20 @@
|
||||
// #target: elf-riscv32
|
||||
module test;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
asm
|
||||
{
|
||||
and $t0, $t1, $t2;
|
||||
or $a0, $a1, $a2;
|
||||
xor $s7, $s6, $s5;
|
||||
andi $s1, $s2, -2048;
|
||||
ori $s3, $s4, 2047;
|
||||
xori $t3, $t4, 1;
|
||||
not $t3, $t4;
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"and t0, t1, t2\0Aor a0, a1, a2\0Axor s7, s6, s5\0Aandi s1, s2, -2048\0Aori s3, s4, 2047\0Axori t3, t4, 1\0Anot t3, t4\0A", "~{x5},~{x9},~{x10},~{x19},~{x23},~{x28}"()
|
||||
21
test/test_suite/asm/asm_intr_rv.c3t
Normal file
21
test/test_suite/asm/asm_intr_rv.c3t
Normal file
@@ -0,0 +1,21 @@
|
||||
// #target: elf-riscv32
|
||||
module test;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int x = 0;
|
||||
void* fp;
|
||||
asm
|
||||
{
|
||||
csrw $mstatus, $a0;
|
||||
csrrsi $zero, $mstatus, 8u;
|
||||
csrrci $zero, $mstatus, 31u;
|
||||
csrrw $zero, $mtvec, fp;
|
||||
mret;
|
||||
wfi;
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"csrw mstatus, a0\0Acsrrsi zero, mstatus, 8\0Acsrrci zero, mstatus, 31\0Acsrrw zero, mtvec, $0\0Amret \0Awfi \0A", "r,~{x0},~{mstatus},~{mtvec}"(ptr %1)
|
||||
30
test/test_suite/asm/asm_jump_rv.c3t
Normal file
30
test/test_suite/asm/asm_jump_rv.c3t
Normal file
@@ -0,0 +1,30 @@
|
||||
// #target: elf-riscv32
|
||||
module test;
|
||||
|
||||
def Sum = fn int(int a, int b);
|
||||
|
||||
fn int sum(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
int result = 0;
|
||||
Sum sum_ptr = ∑
|
||||
|
||||
asm
|
||||
{
|
||||
mv $a0, a;
|
||||
mv $a1, b;
|
||||
jalr $ra, sum_ptr, 0;
|
||||
mv result, $a0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"mv a0, $1\0Amv a1, $2\0Ajalr ra, $3, 0\0Amv $0, a0\0A", "=r,r,r,r,~{x1},~{x10},~{x11}"(i32 %1, i32 %2, ptr %3)
|
||||
20
test/test_suite/asm/asm_load_rv.c3t
Normal file
20
test/test_suite/asm/asm_load_rv.c3t
Normal file
@@ -0,0 +1,20 @@
|
||||
// #target: elf-riscv32
|
||||
module test;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int x = 2;
|
||||
asm
|
||||
{
|
||||
li $s1, -65536;
|
||||
lui $t0, 123456u;
|
||||
auipc $x15, -123456;
|
||||
mv $a0, $a1;
|
||||
lw $a4, [&x];
|
||||
lb $a5, [$a7 - 4];
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"li s1, -65536\0Alui t0, 123456\0Aauipc x15, -123456\0Amv a0, a1\0Alw a4, $0\0Alb a5, -4(a7)\0A", "*m,~{x5},~{x9},~{x10},~{x14},~{x15}"(ptr elementtype(i32) %x)
|
||||
15
test/test_suite/asm/asm_load_rv64.c3t
Normal file
15
test/test_suite/asm/asm_load_rv64.c3t
Normal file
@@ -0,0 +1,15 @@
|
||||
// #target: elf-riscv64
|
||||
module test;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int x = 2;
|
||||
asm
|
||||
{
|
||||
li $s1, -9223372036854775808;
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"li s1, -9223372036854775808\0A", "~{x9}"()
|
||||
19
test/test_suite/asm/asm_math_rv.c3t
Normal file
19
test/test_suite/asm/asm_math_rv.c3t
Normal file
@@ -0,0 +1,19 @@
|
||||
// #target: elf-riscv32
|
||||
module test;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int x = 0;
|
||||
asm
|
||||
{
|
||||
add $t0, $t1, $t2;
|
||||
add x, $t1, $t2;
|
||||
sub $a0, $a1, $a2;
|
||||
addi $s1, $s2, -2048;
|
||||
neg $t3, $t4;
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"add t0, t1, t2\0Aadd $0, t1, t2\0Asub a0, a1, a2\0Aaddi s1, s2, -2048\0Aneg t3, t4\0A", "=r,~{x5},~{x9},~{x10},~{x28}"()
|
||||
18
test/test_suite/asm/asm_set_rv.c3t
Normal file
18
test/test_suite/asm/asm_set_rv.c3t
Normal file
@@ -0,0 +1,18 @@
|
||||
// #target: elf-riscv32
|
||||
module test;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int x = 2;
|
||||
asm
|
||||
{
|
||||
slt $t0, $t1, x;
|
||||
slti $a0, $a1, -2048;
|
||||
sltiu $a0, $a1, 4095u;
|
||||
seqz $s0, $zero;
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"slt t0, t1, $0\0Aslti a0, a1, -2048\0Asltiu a0, a1, 4095\0Aseqz s0, zero\0A", "r,~{x5},~{x8},~{x10}"(i32 %1)
|
||||
19
test/test_suite/asm/asm_shift_rv.c3t
Normal file
19
test/test_suite/asm/asm_shift_rv.c3t
Normal file
@@ -0,0 +1,19 @@
|
||||
// #target: elf-riscv32
|
||||
module test;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
asm
|
||||
{
|
||||
sll $t0, $t1, $t2;
|
||||
slli $s1, $s2, 31u;
|
||||
srl $a0, $a1, $a2;
|
||||
srli $x10, $x11, 0u;
|
||||
sra $t0, $t1, $t2;
|
||||
srai $s1, $s2, 1u;
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"sll t0, t1, t2\0Aslli s1, s2, 31\0Asrl a0, a1, a2\0Asrli x10, x11, 0\0Asra t0, t1, t2\0Asrai s1, s2, 1\0A", "~{x5},~{x9},~{x10}"()
|
||||
17
test/test_suite/asm/asm_store_rv.c3t
Normal file
17
test/test_suite/asm/asm_store_rv.c3t
Normal file
@@ -0,0 +1,17 @@
|
||||
// #target: elf-riscv32
|
||||
module test;
|
||||
|
||||
fn void main(String[] args)
|
||||
{
|
||||
int x = 2;
|
||||
int y = 0;
|
||||
asm
|
||||
{
|
||||
lw $a4, [&x];
|
||||
sw $a4, [&y];
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
"lw a4, $1\0Asw a4, $0\0A", "=*m,*m,~{x14}"(ptr elementtype(i32) %y, ptr elementtype(i32) %x)
|
||||
Reference in New Issue
Block a user