summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJohannes Bucher <johannes.bucher2@student.kit.edu>2019-11-08 17:39:26 +0100
committerJohannes Bucher <johannes.bucher2@student.kit.edu>2019-11-08 17:39:26 +0100
commitc02271be5463c554224566bcf0c49a4f872a0449 (patch)
tree7e714e4e69aaa9f4ea3f484f483cf44dd121c8a3
parentcc5d9e27a5c43be820b1a66867cd4acfcb862247 (diff)
riscv: correctly lower aggregate parameters
Function parameter aggregates are lowered according to the RISC-V ILP32 ABI. Consider that small structs are passed by value when lowering builtin va_arg
-rw-r--r--CMakeLists.txt1
-rw-r--r--ir/be/riscv/riscv_abi.c153
-rw-r--r--ir/be/riscv/riscv_abi.h16
-rw-r--r--ir/be/riscv/riscv_bearch.c47
4 files changed, 198 insertions, 19 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8ce8230..6bc6bfb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -404,6 +404,7 @@ add_backend(mips
ir/be/mips/mips_transform.h
)
add_backend(riscv
+ ir/be/riscv/riscv_abi.c
ir/be/riscv/riscv_bearch.c
ir/be/riscv/riscv_cconv.c
ir/be/riscv/riscv_emitter.c
diff --git a/ir/be/riscv/riscv_abi.c b/ir/be/riscv/riscv_abi.c
new file mode 100644
index 0000000..8e7b239
--- /dev/null
+++ b/ir/be/riscv/riscv_abi.c
@@ -0,0 +1,153 @@
+/**
+ * @file
+ * @brief Implements function parameter lowering for the RISC-V ILP32 ABI
+ * @author Johannes Bucher
+ */
+#include "riscv_abi.h"
+
+#include "riscv_bearch_t.h"
+#include "irmode.h"
+#include "type_t.h"
+
+static ir_mode *fold_classes(ir_mode *c1, ir_mode *c2)
+{
+ if (c1 == c2) {
+ return c1;
+ } else if (c1 == mode_BAD) {
+ return c2;
+ } else if (c2 == mode_BAD) {
+ return c1;
+ } else {
+ panic("TODO");
+ }
+}
+
+static ir_mode *classify_slice_for_ilp32(ir_type const *const tp, unsigned min, unsigned max);
+
+static ir_mode *classify_compound_by_members(ir_type const *const tp, unsigned min, unsigned max)
+{
+ unsigned n = get_compound_n_members(tp);
+ ir_mode *current_class = mode_BAD;
+ for (unsigned i = 0; i < n; i++) {
+ ir_entity *member = get_compound_member(tp, i);
+ ir_type *member_type = get_entity_type(member);
+ unsigned member_size = get_type_size(member_type);
+
+ unsigned member_begin = get_entity_offset(member);
+ unsigned member_end = member_begin + member_size;
+
+ // Is the member (at least partially) between min and max?
+ if (min < member_end && max > member_begin) {
+ if (get_entity_aligned(member) == align_non_aligned) {
+ return mode_M;
+ }
+ unsigned min_in_member = min <= member_begin ? 0 : min - member_begin;
+ unsigned max_in_member = member_end < max ? member_size : max - member_begin;
+ ir_mode *member_class = classify_slice_for_ilp32(member_type, min_in_member, max_in_member);
+ current_class = fold_classes(current_class, member_class);
+ }
+ }
+ return current_class;
+}
+
+static ir_mode *classify_slice_for_ilp32(ir_type const *const tp, unsigned min, unsigned max)
+{
+ switch(get_type_opcode(tp)) {
+ case tpo_class:
+ case tpo_struct:
+ case tpo_union:
+ return classify_compound_by_members(tp, min, max);
+
+ case tpo_array: {
+ ir_type *elem_type = get_array_element_type(tp);
+ if (min < get_type_size(tp)) {
+ // We are in the array
+ size_t elem_size = get_type_size(elem_type);
+ if (min >= elem_size) {
+ // ... but past the first element. Shift the slice range down.
+ unsigned new_min = min % elem_size;
+ unsigned new_max = new_min + (max - min);
+ return classify_slice_for_ilp32(elem_type, new_min, new_max);
+ } else {
+ return classify_slice_for_ilp32(elem_type, min, max);
+ }
+ } else {
+ return mode_BAD;
+ }
+ }
+ case tpo_primitive: {
+ ir_mode *mode = get_type_mode(tp);
+ if (min >= get_type_size(tp)) {
+ return mode_BAD;
+ } else if (mode_is_float(mode)) {
+ panic("RISC-V ILP32 ABI has no hardware floating point support");
+ } else {
+ return mode_Iu;
+ }
+ }
+ case tpo_pointer:
+ if (min >= get_type_size(tp)) {
+ return mode_BAD;
+ } else {
+ return mode_Iu;
+ }
+
+ case tpo_code:
+ case tpo_method:
+ case tpo_segment:
+ case tpo_uninitialized:
+ case tpo_unknown:
+ break;
+ }
+ panic("invalid type");
+}
+
+static aggregate_spec_t classify_for_ilp32(ir_type const *const type) {
+ if (get_type_size(type) > 2 * RISCV_REGISTER_SIZE) {
+ return (aggregate_spec_t) {
+ .length = 1,
+ .modes = { mode_P },
+ };
+ }
+
+ aggregate_spec_t result = {
+ .length = 0,
+ .modes = { },
+ };
+
+ for (unsigned i = 0; i < 2; i++) {
+ ir_mode *c = classify_slice_for_ilp32(type, RISCV_REGISTER_SIZE * i, RISCV_REGISTER_SIZE * (i + 1));
+ result.modes[i] = c;
+ if (c != mode_BAD) {
+ result.length++;
+ }
+ }
+
+ /* if type has 8 byte alignment and result is two (4-byte) mode_Iu slices we have to convert this into
+ * a single 8 byte mode_Lu slice to ensure correct alignment when passing the argument.
+ * (Variadic arguments with 2*XLEN-bit alignment and size at most 2*XLEN bits are passed in an aligned register pair) */
+ if (result.length == 2 && result.modes[0] == mode_Iu && result.modes[1] == mode_Iu &&
+ (get_type_alignment(type) == 2 * RISCV_REGISTER_SIZE)) {
+ result.length = 1;
+ result.modes[0] = mode_Lu;
+ }
+
+ return result;
+}
+
+aggregate_spec_t riscv_lower_parameter(void *env, ir_type const *type) {
+ (void)env;
+
+ if (is_aggregate_type(type)) {
+ return classify_for_ilp32(type);
+ } else {
+ return (aggregate_spec_t) {
+ .length = 1,
+ .modes = { get_type_mode(type) },
+ };
+ }
+}
+
+aggregate_spec_t riscv_lower_result(void *env, ir_type const *type) {
+ return riscv_lower_parameter(env, type);
+}
diff --git a/ir/be/riscv/riscv_abi.h b/ir/be/riscv/riscv_abi.h
new file mode 100644
index 0000000..ee82417
--- /dev/null
+++ b/ir/be/riscv/riscv_abi.h
@@ -0,0 +1,16 @@
+/**
+ * @file
+ * @brief Implements function parameter lowering for the RISC-V ILP32 ABI
+ * @author Johannes Bucher
+ */
+#ifndef LIBFIRM_RISCV_ABI_H
+#define LIBFIRM_RISCV_ABI_H
+
+#include "firm_types.h"
+#include "lower_calls.h"
+
+aggregate_spec_t riscv_lower_parameter(void *env, ir_type const *type);
+
+aggregate_spec_t riscv_lower_result(void *env, ir_type const *type);
+
+#endif //LIBFIRM_RISCV_ABI_H
diff --git a/ir/be/riscv/riscv_bearch.c b/ir/be/riscv/riscv_bearch.c
index acd1423..1e760cd 100644
--- a/ir/be/riscv/riscv_bearch.c
+++ b/ir/be/riscv/riscv_bearch.c
@@ -16,7 +16,6 @@
#include "bestack.h"
#include "betranshlp.h"
#include "beutil.h"
-#include "bevarargs.h"
#include "gen_riscv_new_nodes.h"
#include "gen_riscv_regalloc_if.h"
#include "irarch.h"
@@ -30,6 +29,7 @@
#include "lower_calls.h"
#include "lowering.h"
#include "platform_t.h"
+#include "riscv_abi.h"
#include "riscv_emitter.h"
#include "riscv_lower64.h"
#include "riscv_transform.h"
@@ -426,24 +426,34 @@ static void riscv32_lower_va_arg(ir_node *node)
ir_mode *apmode = get_type_mode(aptype);
ir_node *res;
ir_node *new_mem;
- if (apmode) {
- goto load;
- } else {
+ ir_type *const original_aptype = aptype;
+
+ if (!apmode) {
+ // compounds are generally passed by reference
+ assert(is_compound_type(aptype));
apmode = mode_P;
aptype = get_type_for_mode(apmode);
- load:;
- round_up = round_up2(get_type_alignment(aptype), RISCV_PARAM_STACK_ALIGN);
-
- if (round_up > RISCV_PARAM_STACK_ALIGN) {
- ir_node *const align_min_1 = new_rd_Const_long(dbgi, irg, offset_mode, (int) round_up - 1);
- ir_node *const align_add = new_rd_Add(dbgi, block, ap, align_min_1);
- ir_node *const align_neg = new_rd_Const_long(dbgi, irg, offset_mode, -round_up);
- ir_node *const align_add_conv = new_rd_Conv(dbgi, block, align_add, offset_mode);
- ir_node *const align_and = new_rd_And(dbgi, block, align_add_conv, align_neg);
- ir_node *const align_and_conv = new_rd_Conv(dbgi, block, align_and, mode_P);
- ap = align_and_conv;
- }
+ }
+ // align the va_arg pointer according to the argument alignment
+ round_up = round_up2(get_type_alignment(aptype), RISCV_PARAM_STACK_ALIGN);
+ if (round_up > RISCV_PARAM_STACK_ALIGN) {
+ // ap = (ap + (round_up - 1)) & -(round_up)
+ ir_node *const align_min_1 = new_rd_Const_long(dbgi, irg, offset_mode, (int) round_up - 1);
+ ir_node *const align_add = new_rd_Add(dbgi, block, ap, align_min_1);
+ ir_node *const align_neg = new_rd_Const_long(dbgi, irg, offset_mode, -round_up);
+ ir_node *const align_add_conv = new_rd_Conv(dbgi, block, align_add, offset_mode);
+ ir_node *const align_and = new_rd_And(dbgi, block, align_add_conv, align_neg);
+ ir_node *const align_and_conv = new_rd_Conv(dbgi, block, align_and, mode_P);
+ ap = align_and_conv;
+ }
+
+ if (is_compound_type(original_aptype) && get_type_size(original_aptype) <= 2 * RISCV_REGISTER_SIZE) {
+ // structs with size <= 2*XLEN are passed by value, not by reference
+ res = ap;
+ new_mem = node_mem;
+ aptype = original_aptype;
+ } else {
ir_node *const load = new_rd_Load(dbgi, block, node_mem, ap, apmode, aptype, cons_none);
res = new_r_Proj(load, apmode, pn_Load_res);
new_mem = new_r_Proj(load, mode_M, pn_Load_M);
@@ -462,10 +472,9 @@ static void riscv_lower_for_target(void)
ir_arch_lower(&riscv_arch_dep);
be_after_irp_transform("lower-arch-dep");
- //TODO correct lowering of compounds
lower_calls_with_compounds(LF_RETURN_HIDDEN,
- lower_aggregates_as_pointers, NULL,
- lower_aggregates_as_pointers, NULL,
+ riscv_lower_parameter, NULL,
+ riscv_lower_result, NULL,
reset_stateless_abi);
be_after_irp_transform("lower-calls");