Add RISC-V block asm support

This commit is contained in:
Chuck Benedict
2024-08-20 12:43:55 -07:00
committed by Christoffer Lerno
parent d32861193b
commit 05ab0707fc
18 changed files with 749 additions and 11 deletions

View File

@@ -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
View 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" };

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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:

View File

@@ -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;

View File

@@ -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)

View 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}"()

View 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)

View 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 = &sum;
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)

View 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)

View 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}"()

View 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}"()

View 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)

View 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}"()

View 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)