summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJohannes Bucher <johannes.bucher2@student.kit.edu>2019-03-06 16:19:07 +0100
committerJohannes Bucher <johannes.bucher2@student.kit.edu>2019-03-12 10:01:09 +0100
commit02952f4342b2ffa40ba33292770416625ea77681 (patch)
tree5f1b4a9522f1808950f1ed3c0bbaf2aa5968db39
parent9f4c8ce4103b25dc949f46a9020264bbcfd5f97f (diff)
riscv: fix invalid assembler errors due to too large immediatesriscv
RISC-V I-type and S-type instruction formats only accept 12 bit immediates as operands. Address offsets, constants, and IncSP values which exceed the 12 bit range are now correctly transformed into multiple instructions (lui + addi) The t0 register is now set to ignore, this allows building immediates using multiple instructions after register allocation.
-rw-r--r--CMakeLists.txt1
-rw-r--r--ir/be/riscv/riscv_bearch.c15
-rw-r--r--ir/be/riscv/riscv_bearch_t.h24
-rw-r--r--ir/be/riscv/riscv_emitter.c26
-rw-r--r--ir/be/riscv/riscv_finish.c141
-rw-r--r--ir/be/riscv/riscv_new_nodes.c1
-rw-r--r--ir/be/riscv/riscv_spec.pl10
-rw-r--r--ir/be/riscv/riscv_transform.c36
-rw-r--r--ir/be/riscv/riscv_transform.h2
9 files changed, 229 insertions, 27 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2ea7ba5..065687a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -405,6 +405,7 @@ add_backend(riscv
ir/be/riscv/riscv_bearch.c
ir/be/riscv/riscv_cconv.c
ir/be/riscv/riscv_emitter.c
+ ir/be/riscv/riscv_finish.c
ir/be/riscv/riscv_lower64.c
ir/be/riscv/riscv_new_nodes.c
ir/be/riscv/riscv_nodes_attr.c
diff --git a/ir/be/riscv/riscv_bearch.c b/ir/be/riscv/riscv_bearch.c
index 74e1274..2eedaa1 100644
--- a/ir/be/riscv/riscv_bearch.c
+++ b/ir/be/riscv/riscv_bearch.c
@@ -42,6 +42,19 @@ static ir_settings_arch_dep_t const riscv_arch_dep = {
.max_bits_for_mulh = RISCV_MACHINE_SIZE,
};
+/**
+ * Splits a 32 bit immediate value into a 20 bit hi and a 12 bit lo part, which can be realised by lui and addi
+ * instructions.
+ */
+riscv_hi_lo_imm calc_hi_lo(int32_t val) {
+ int32_t hi = ((uint32_t) val >> 12) + ((uint32_t) val >> 11 & 1);
+ if (hi >= 1048576) { //2^20
+ hi = 0;
+ }
+ int32_t const lo = (uint32_t) val - (hi << 12);
+ return (riscv_hi_lo_imm) {hi, lo};
+}
+
static void riscv_init_asm_constraints(void)
{
be_set_constraint_support(ASM_CONSTRAINT_FLAG_SUPPORTS_MEMOP, "m");
@@ -201,6 +214,7 @@ static void riscv_sp_sim(ir_node *const node, stack_pointer_state_t *const state
if (is_riscv_irn(node)) {
switch ((riscv_opcodes)get_riscv_irn_opcode(node)) {
case iro_riscv_addi:
+ case iro_riscv_FrameAddr:
case iro_riscv_lb:
case iro_riscv_lbu:
case iro_riscv_lh:
@@ -253,6 +267,7 @@ static void riscv_generate_code(FILE *const output, char const *const cup_name)
birg->non_ssa_regs = NULL;
be_sim_stack_pointer(irg, 0, 3, &riscv_sp_sim);
+ riscv_finish_graph(irg);
be_handle_2addr(irg, NULL);
riscv_emit_function(irg);
diff --git a/ir/be/riscv/riscv_bearch_t.h b/ir/be/riscv/riscv_bearch_t.h
index 1ce0eb4..7ce4c20 100644
--- a/ir/be/riscv/riscv_bearch_t.h
+++ b/ir/be/riscv/riscv_bearch_t.h
@@ -8,4 +8,28 @@
#define RISCV_MACHINE_SIZE 32
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "firm_types.h"
+
+static inline bool is_uimm5(long const val)
+{
+ return 0 <= val && val < 32;
+}
+
+static inline bool is_simm12(long const val)
+{
+ return -2048 <= val && val < 2048;
+}
+
+typedef struct riscv_hi_lo_imm {
+ int32_t hi;
+ int32_t lo;
+} riscv_hi_lo_imm;
+
+riscv_hi_lo_imm calc_hi_lo(int32_t val);
+
+void riscv_finish_graph(ir_graph *irg);
+
#endif
diff --git a/ir/be/riscv/riscv_emitter.c b/ir/be/riscv/riscv_emitter.c
index d8d5614..6df475c 100644
--- a/ir/be/riscv/riscv_emitter.c
+++ b/ir/be/riscv/riscv_emitter.c
@@ -18,6 +18,7 @@
#include "gen_riscv_emitter.h"
#include "gen_riscv_regalloc_if.h"
#include "riscv_nodes_attr.h"
+#include "riscv_bearch_t.h"
#include "util.h"
static void emit_immediate_val(char const *const prefix, ir_entity *const ent, int32_t const val)
@@ -180,6 +181,7 @@ static void emit_be_Copy(ir_node const *const node)
static void emit_be_IncSP(ir_node const *const node)
{
int const offs = -be_get_IncSP_offset(node);
+ assert(is_simm12(offs));
riscv_emitf(node, "addi\t%D0, %S0, %d", offs);
}
@@ -229,18 +231,28 @@ static void emit_riscv_switch(ir_node const *const node)
be_emit_jump_table(node, &attr->swtch, mode_P, emit_jumptable_target);
}
+static void emit_riscv_FrameAddr(const ir_node *node)
+{
+ const riscv_immediate_attr_t *attr = get_riscv_immediate_attr_const(node);
+ int32_t offset = attr->val;
+
+ assert(is_simm12((long)offset));
+ riscv_emitf(node, "addi\t%D0, %S0, %d", (int)offset);
+}
+
static void riscv_register_emitters(void)
{
be_init_emitters();
riscv_register_spec_emitters();
- be_set_emitter(op_be_Asm, emit_be_ASM);
- be_set_emitter(op_be_Copy, emit_be_Copy);
- be_set_emitter(op_be_IncSP, emit_be_IncSP);
- be_set_emitter(op_be_Perm, emit_be_Perm);
- be_set_emitter(op_riscv_bcc, emit_riscv_bcc);
- be_set_emitter(op_riscv_j, emit_riscv_j);
- be_set_emitter(op_riscv_switch, emit_riscv_switch);
+ be_set_emitter(op_be_Asm, emit_be_ASM);
+ be_set_emitter(op_be_Copy, emit_be_Copy);
+ be_set_emitter(op_be_IncSP, emit_be_IncSP);
+ be_set_emitter(op_be_Perm, emit_be_Perm);
+ be_set_emitter(op_riscv_FrameAddr, emit_riscv_FrameAddr);
+ be_set_emitter(op_riscv_bcc, emit_riscv_bcc);
+ be_set_emitter(op_riscv_j, emit_riscv_j);
+ be_set_emitter(op_riscv_switch, emit_riscv_switch);
}
static void riscv_gen_block(ir_node *const block)
diff --git a/ir/be/riscv/riscv_finish.c b/ir/be/riscv/riscv_finish.c
new file mode 100644
index 0000000..cb8f583
--- /dev/null
+++ b/ir/be/riscv/riscv_finish.c
@@ -0,0 +1,141 @@
+/*
+ * This file is part of libFirm.
+ * Copyright (C) 2018 University of Karlsruhe.
+ */
+
+/**
+ * @file
+ * @brief Peephole optimization and legalization of a RISC-V function
+ * @author Johannes Bucher
+ */
+#include "riscv_bearch_t.h"
+
+
+#include "besched.h"
+#include "gen_riscv_new_nodes.h"
+#include "be2addr.h"
+#include "beirg.h"
+#include "bepeephole.h"
+#include "riscv_transform.h"
+#include "../../../build/gen/ir/be/riscv/gen_riscv_regalloc_if.h"
+
+
+/**
+ * Creates a constant from an immediate value.
+ */
+static ir_node *create_constant_from_immediate(ir_node *node, int val)
+{
+ dbg_info *dbgi = get_irn_dbg_info(node);
+ ir_node *block = get_nodes_block(node);
+
+ riscv_hi_lo_imm imm = calc_hi_lo(val);
+ ir_node *res;
+ if (imm.hi != 0) {
+ res = new_bd_riscv_lui(dbgi, block, NULL, imm.hi);
+ arch_set_irn_register(res, &riscv_registers[REG_T0]);
+ sched_add_before(node, res);
+ } else {
+ ir_graph *const irg = get_irn_irg(node);
+ res = get_Start_zero(irg);
+ }
+ if (imm.lo != 0) {
+ res = new_bd_riscv_addi(dbgi, block, res, NULL, imm.lo);
+ arch_set_irn_register(res, &riscv_registers[REG_T0]);
+ sched_add_before(node, res);
+ }
+ return res;
+}
+
+/**
+ * Adjust sp-relative offsets.
+ *
+ * Split into multiple instructions if offset exceeds RISC-V 12 bit immediate range.
+ */
+static void finish_riscv_FrameAddr(ir_node *node)
+{
+ riscv_immediate_attr_t *attr = get_riscv_immediate_attr(node);
+ int offset = attr->val;
+
+ if (!is_simm12(offset)) {
+ ir_node *base = get_irn_n(node, n_riscv_FrameAddr_base);
+ dbg_info *dbgi = get_irn_dbg_info(node);
+ ir_node *block = get_nodes_block(node);
+ ir_node *constant = create_constant_from_immediate(node, offset);
+ ir_node *new_frameaddr = new_bd_riscv_add(dbgi, block, base, constant);
+ const arch_register_t *reg = arch_get_irn_register(node);
+
+ arch_set_irn_register(new_frameaddr, reg);
+ be_peephole_replace(node, new_frameaddr);
+ }
+}
+
+/**
+ * The address offset immediate at load and store instructions is limited to 12 bit.
+ * Adds an additional lui and add nodes to support large offsets.
+ */
+static void finish_riscv_load_store_offsets(ir_node *node) {
+ riscv_immediate_attr_t *const imm = get_riscv_immediate_attr(node);
+ if (!is_simm12(imm->val)) {
+ dbg_info *dbgi = get_irn_dbg_info(node);
+ ir_node *block = get_nodes_block(node);
+ ir_node *res;
+
+ riscv_hi_lo_imm hi_lo_imm = calc_hi_lo(imm->val);
+ if (hi_lo_imm.hi != 0) {
+ ir_node *lui = new_bd_riscv_lui(dbgi, block, NULL, hi_lo_imm.hi);
+ arch_set_irn_register(lui, &riscv_registers[REG_T0]);
+ sched_add_before(node, lui);
+ ir_node *const base = get_irn_n(node, n_riscv_sw_base);
+ res = new_bd_riscv_add(dbgi, block, base, lui);
+ arch_set_irn_register(res, &riscv_registers[REG_T0]);
+ sched_add_before(node, res);
+ } else {
+ res = node;
+ }
+ set_irn_n(node, n_riscv_sw_base, res);
+ imm->val = hi_lo_imm.lo;
+ }
+}
+
+/**
+ * RISC-V immediates are limited. Split IncSP with bigger immediates if necessary.
+ */
+static void finish_be_IncSP(ir_node *node)
+{
+ int offset = -be_get_IncSP_offset(node);
+
+ /* we might have to break the IncSP apart if the constant has become too big */
+ if (!is_simm12(offset)) {
+ ir_node *sp = be_get_IncSP_pred(node);
+ dbg_info *dbgi = get_irn_dbg_info(node);
+ ir_node *block = get_nodes_block(node);
+ ir_node *constant = create_constant_from_immediate(node, offset);
+ ir_node *add = new_bd_riscv_add(dbgi, block, sp, constant);
+
+ arch_set_irn_register(add, &riscv_registers[REG_SP]);
+ be_peephole_replace(node, add);
+ }
+}
+
+void riscv_finish_graph(ir_graph *irg)
+{
+ /* perform peephole optimizations */
+ ir_clear_opcodes_generic_func();
+
+ be_peephole_opt(irg);
+
+ /* perform legalizations (mostly fix nodes with too big immediates) */
+ ir_clear_opcodes_generic_func();
+ register_peephole_optimization(op_riscv_FrameAddr, finish_riscv_FrameAddr);
+ register_peephole_optimization(op_riscv_lb, finish_riscv_load_store_offsets);
+ register_peephole_optimization(op_riscv_lbu, finish_riscv_load_store_offsets);
+ register_peephole_optimization(op_riscv_lh, finish_riscv_load_store_offsets);
+ register_peephole_optimization(op_riscv_lhu, finish_riscv_load_store_offsets);
+ register_peephole_optimization(op_riscv_lw, finish_riscv_load_store_offsets);
+ register_peephole_optimization(op_riscv_sb, finish_riscv_load_store_offsets);
+ register_peephole_optimization(op_riscv_sh, finish_riscv_load_store_offsets);
+ register_peephole_optimization(op_riscv_sw, finish_riscv_load_store_offsets);
+
+ register_peephole_optimization(op_be_IncSP, finish_be_IncSP);
+ be_peephole_opt(irg);
+}
diff --git a/ir/be/riscv/riscv_new_nodes.c b/ir/be/riscv/riscv_new_nodes.c
index 051f192..94f6e9d 100644
--- a/ir/be/riscv/riscv_new_nodes.c
+++ b/ir/be/riscv/riscv_new_nodes.c
@@ -125,6 +125,7 @@ void riscv_dump_node(FILE *const F, ir_node const *const n, dump_reason_t const
case iro_riscv_and:
case iro_riscv_div:
case iro_riscv_divu:
+ case iro_riscv_FrameAddr:
case iro_riscv_ijmp:
case iro_riscv_j:
case iro_riscv_jalr:
diff --git a/ir/be/riscv/riscv_spec.pl b/ir/be/riscv/riscv_spec.pl
index 1488eaf..b36af18 100644
--- a/ir/be/riscv/riscv_spec.pl
+++ b/ir/be/riscv/riscv_spec.pl
@@ -237,4 +237,14 @@ xor => { template => $binOp },
xori => { template => $immediateOp },
+FrameAddr => {
+ op_flags => [ "constlike" ],
+ irn_flags => [ "rematerializable" ],
+ attr => "ir_entity *ent, int32_t val",
+ in_reqs => [ "cls-gp" ],
+ out_reqs => [ "cls-gp" ],
+ ins => [ "base" ],
+ attr_type => "riscv_immediate_attr_t",
+},
+
);
diff --git a/ir/be/riscv/riscv_transform.c b/ir/be/riscv/riscv_transform.c
index 847f79e..f2c2f18 100644
--- a/ir/be/riscv/riscv_transform.c
+++ b/ir/be/riscv/riscv_transform.c
@@ -5,6 +5,7 @@
#include "riscv_transform.h"
+#include "becconv.h"
#include "beirg.h"
#include "benode.h"
#include "betranshlp.h"
@@ -20,6 +21,10 @@ static riscv_calling_convention_t cur_cconv;
static be_stack_env_t stack_env;
+static const unsigned ignore_regs[] = {
+ REG_T0,
+};
+
static unsigned const callee_saves[] = {
REG_S0,
REG_S1,
@@ -37,7 +42,6 @@ static unsigned const callee_saves[] = {
static unsigned const caller_saves[] = {
REG_RA,
- REG_T0,
REG_T1,
REG_T2,
REG_A0,
@@ -59,21 +63,11 @@ static ir_node *get_Start_sp(ir_graph *const irg)
return be_get_Start_proj(irg, &riscv_registers[REG_SP]);
}
-static ir_node *get_Start_zero(ir_graph *const irg)
+ir_node *get_Start_zero(ir_graph *const irg)
{
return be_get_Start_proj(irg, &riscv_registers[REG_ZERO]);
}
-static inline bool is_uimm5(long const val)
-{
- return 0 <= val && val < 32;
-}
-
-static inline bool is_simm12(long const val)
-{
- return -2048 <= val && val < 2048;
-}
-
typedef struct riscv_addr {
ir_node *base;
ir_entity *ent;
@@ -189,8 +183,9 @@ static bool riscv_check_immediate_constraint(long const val, char const imm_type
case 'g':
case 'i':
case 'n': return true;
+ default:
+ panic("invalid immediate constraint found");
}
- panic("invalid immediate constraint found");
}
static bool riscv_match_immediate(riscv_asm_operand_t *const operand, ir_node *const node, char const imm_type)
@@ -557,17 +552,16 @@ static ir_node *gen_Const(ir_node *const node)
dbg_info *const dbgi = get_irn_dbg_info(node);
ir_node *const block = be_transform_nodes_block(node);
+ riscv_hi_lo_imm imm = calc_hi_lo((int32_t)val);
ir_node *res;
- int32_t const hi = ((uint32_t)val >> 12) + ((uint32_t)val >> 11 & 1);
- if (hi != 0) {
- res = new_bd_riscv_lui(dbgi, block, NULL, hi);
+ if (imm.hi != 0) {
+ res = new_bd_riscv_lui(dbgi, block, NULL, imm.hi);
} else {
ir_graph *const irg = get_irn_irg(node);
res = get_Start_zero(irg);
}
- int32_t const lo = (uint32_t)val - (hi << 12);
- if (lo != 0)
- res = new_bd_riscv_addi(dbgi, block, res, NULL, lo);
+ if (imm.lo != 0)
+ res = new_bd_riscv_addi(dbgi, block, res, NULL, imm.lo);
return res;
}
}
@@ -657,7 +651,7 @@ static ir_node *gen_Member(ir_node *const node)
ir_node *const block = be_transform_nodes_block(node);
ir_node *const frame = be_transform_node(ptr);
ir_entity *const ent = get_Member_entity(node);
- return new_bd_riscv_addi(dbgi, block, frame, ent, 0);
+ return new_bd_riscv_FrameAddr(dbgi, block, frame, ent, 0);
}
static ir_node *gen_Minus(ir_node *const node)
@@ -1177,6 +1171,8 @@ static void riscv_set_allocatable_regs(ir_graph *const irg)
rbitset_set(a, caller_saves[i]);
}
birg->allocatable_regs = a;
+
+ be_cconv_rem_regs(birg->allocatable_regs, ignore_regs, ARRAY_SIZE(ignore_regs));
}
void riscv_transform_graph(ir_graph *const irg)
diff --git a/ir/be/riscv/riscv_transform.h b/ir/be/riscv/riscv_transform.h
index 6b1ae63..daca3f6 100644
--- a/ir/be/riscv/riscv_transform.h
+++ b/ir/be/riscv/riscv_transform.h
@@ -8,6 +8,8 @@
#include "firm_types.h"
+ir_node *get_Start_zero(ir_graph *irg);
+
void riscv_transform_graph(ir_graph *irg);
#endif