Commit 9cf2541c authored by Christoph Mallon's avatar Christoph Mallon
Browse files

Merge branch 'mips'.

There is still much to add and improve.
But it should be sufficient to compile some small programs.
parents 380188c4 453ec26a
......@@ -67,7 +67,7 @@ libfirm_BUILDDIRS = $(sort $(dir $(libfirm_OBJECTS))) $(addprefix $(gendir)/,
firm: $(libfirm_dll) $(libfirm_a)
# backends
backends = amd64 arm ia32 sparc TEMPLATE
backends = amd64 arm ia32 mips sparc TEMPLATE
EMITTER_GENERATOR = $(srcdir)/ir/be/scripts/generate_emitter.pl
REGALLOC_IF_GENERATOR = $(srcdir)/ir/be/scripts/generate_regalloc_if.pl
......
......@@ -48,6 +48,7 @@ libFirm 1.22.1 (2016-01-07)
* opt: Add local optimization `a * b [-1 <= b <= 0] -> -(a & b)`
* sparc: More strict checking of modifiers of placeholders in asm templates
* arm: Support inline asm with with constraints `I`, `J`, `K`, `L`, `M`, `Q`, `g`, `i`, `l`, `m`, `n` and `r` as well as modifiers `B`, `C` and `c`
* mips: Add new backend
* Bugfixes
libFirm 1.22.0 (2015-12-31)
......
......@@ -482,12 +482,6 @@ emit_R:
break;
}
case 'd': {
int const num = va_arg(ap, int);
be_emit_irprintf("%d", num);
break;
}
case 's': {
char const *const str = va_arg(ap, char const*);
be_emit_string(str);
......
......@@ -23,7 +23,6 @@
* %O <node> offset
* %R arch_register_t const* register
* %Sx <node> source register x
* %d signed int signed int
* %s char const* string
* %u unsigned int unsigned int
*
......
......@@ -295,12 +295,6 @@ void arm_emitf(const ir_node *node, const char *format, ...)
break;
}
case 'd': {
int num = va_arg(ap, int);
be_emit_irprintf("%d", num);
break;
}
case 's': {
const char *string = va_arg(ap, const char *);
be_emit_string(string);
......
......@@ -32,7 +32,6 @@
* %m ir_mode* fpa mode postfix
* %s const char* string
* %u unsigned int unsigned int
* %d signed int signed int
* %X signed int signed int (hexadecimal)
*/
void arm_emitf(const ir_node *node, const char *format, ...);
......
......@@ -1575,20 +1575,6 @@ static ir_node *gen_Proj_Proj_Start(ir_node *node)
}
}
/**
* Finds number of output value of a mode_T node which is constrained to
* a single specific register.
*/
static int find_out_for_reg(ir_node *node, const arch_register_t *reg)
{
be_foreach_out(node, o) {
const arch_register_req_t *req = arch_get_irn_register_req_out(node, o);
if (req == reg->single_req)
return o;
}
return -1;
}
static ir_node *gen_Proj_Proj_Call(ir_node *node)
{
unsigned pn = get_Proj_num(node);
......@@ -1600,10 +1586,7 @@ static ir_node *gen_Proj_Proj_Call(ir_node *node)
const reg_or_stackslot_t *res = &cconv->results[pn];
assert(res->reg0 != NULL && res->reg1 == NULL);
int regn = find_out_for_reg(new_call, res->reg0);
if (regn < 0) {
panic("Internal error in calling convention for return %+F", node);
}
unsigned const regn = be_get_out_for_reg(new_call, res->reg0);
arm_free_calling_convention(cconv);
......
......@@ -106,6 +106,10 @@ bool be_is_fallthrough(ir_node const *jmp);
} else if (*fmt == 'L') { \
++fmt; \
be_emit_cfop_target(va_arg(ap, ir_node const*)); \
} else if (*fmt == 'd') { \
++fmt; \
int const num = va_arg(ap, int); \
be_emit_irprintf("%d", num); \
} else
#define BE_EMIT_JMP(arch, node, name, jmp) \
......
......@@ -31,6 +31,7 @@ void be_init_arch_TEMPLATE(void);
void be_init_arch_amd64(void);
void be_init_arch_arm(void);
void be_init_arch_ia32(void);
void be_init_arch_mips(void);
void be_init_arch_sparc(void);
void be_init_blocksched(void);
void be_init_chordal(void);
......@@ -97,6 +98,7 @@ void be_init_modules(void)
/* in the following groups the first one is the default */
be_init_arch_ia32();
be_init_arch_arm();
be_init_arch_mips();
be_init_arch_sparc();
be_init_arch_amd64();
be_init_arch_TEMPLATE();
......
......@@ -953,3 +953,13 @@ ir_node *be_make_Sync(ir_node *const block, int const arity, ir_node **const ins
arity == 1 ? ins[0] :
new_r_Sync(block, arity, ins);
}
unsigned be_get_out_for_reg(ir_node const *const node, arch_register_t const *const reg)
{
be_foreach_out(node, o) {
arch_register_req_t const *const req = arch_get_irn_register_req_out(node, o);
if (req == reg->single_req)
return o;
}
panic("register requirement not found");
}
......@@ -193,4 +193,10 @@ static inline bool be_mode_needs_gp_reg(ir_mode *const mode)
return get_mode_arithmetic(mode) == irma_twos_complement;
}
/**
* Finds number of output value of a node which is constrained to a single
* specific register.
*/
unsigned be_get_out_for_reg(ir_node const *node, arch_register_t const *reg);
#endif
......@@ -568,16 +568,6 @@ emit_S:
}
break;
case 'd':
if (mod & EMIT_LONG) {
long num = va_arg(ap, long);
be_emit_irprintf("%ld", num);
} else {
int num = va_arg(ap, int);
be_emit_irprintf("%d", num);
}
break;
default:
unknown:
panic("unknown format conversion");
......
......@@ -39,13 +39,12 @@
* %Sx <node> source register x
* %s char const* string
* %u unsigned int unsigned int
* %d signed int signed int
*
* x starts at 0
* # modifier for %ASx, %D, %R, and %S uses ls mode of node to alter register width
* # modifier for %M for extend suffix
* * modifier does not prefix immediates with $, but AM with *
* l modifier for %lu and %ld
* l modifier for %lu
* > modifier to output high 8bit register (ah, bh)
* < modifier to output low 8bit register (al, bl)
* , modifier output comma after operand, leave out operand if it is 1
......
/*
* This file is part of libFirm.
* Copyright (C) 2017 University of Karlsruhe.
*/
#include "be_t.h"
#include "beirg.h"
#include "bemodule.h"
#include "bera.h"
#include "besched.h"
#include "bespillslots.h"
#include "bestack.h"
#include "betranshlp.h"
#include "gen_mips_new_nodes.h"
#include "gen_mips_regalloc_if.h"
#include "irarch_t.h"
#include "iredges.h"
#include "irgwalk.h"
#include "irprog_t.h"
#include "lower_dw.h"
#include "mips_bearch_t.h"
#include "mips_emitter.h"
#include "mips_transform.h"
static int mips_is_mux_allowed(ir_node *const sel, ir_node *const mux_false, ir_node *const mux_true)
{
(void)sel;
(void)mux_false;
(void)mux_true;
return false;
}
static ir_settings_arch_dep_t const mips_arch_dep = {
.also_use_subs = true,
.maximum_shifts = 4,
.highest_shift_amount = MIPS_MACHINE_SIZE - 1,
.evaluate = NULL,
.allow_mulhs = true,
.allow_mulhu = true,
.max_bits_for_mulh = MIPS_MACHINE_SIZE,
};
static backend_params mips_backend_params = {
.experimental = "the MIPS backend is highly experimental and unfinished",
.byte_order_big_endian = false,
.pic_supported = false,
.unaligned_memaccess_supported = false,
.modulo_shift = MIPS_MACHINE_SIZE,
.dep_param = &mips_arch_dep,
.allow_ifconv = &mips_is_mux_allowed,
.machine_size = MIPS_MACHINE_SIZE,
.mode_float_arithmetic = NULL, /* will be set later */ // TODO
.type_long_double = NULL, /* will be set later */ // TODO
.stack_param_align = 4,
.float_int_overflow = ir_overflow_indefinite,
};
static void mips_init_asm_constraints(void)
{
be_set_constraint_support(ASM_CONSTRAINT_FLAG_SUPPORTS_MEMOP, "Rm");
be_set_constraint_support(ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER, "cdrvy");
be_set_constraint_support(ASM_CONSTRAINT_FLAG_SUPPORTS_ANY, "g");
be_set_constraint_support(ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE, "IJKLMNOPin");
}
static void mips_init(void)
{
ir_mode *const ptr_mode = new_reference_mode("p32", MIPS_MACHINE_SIZE, MIPS_MACHINE_SIZE);
set_modeP(ptr_mode);
mips_init_asm_constraints();
mips_create_opcodes();
mips_register_init();
}
static void mips_finish(void)
{
mips_free_opcodes();
}
static const backend_params *mips_get_libfirm_params(void)
{
return &mips_backend_params;
}
static void mips_select_instructions(ir_graph *const irg)
{
be_timer_push(T_CODEGEN);
mips_transform_graph(irg);
be_timer_pop(T_CODEGEN);
be_dump(DUMP_BE, irg, "code-selection");
place_code(irg);
be_dump(DUMP_BE, irg, "place");
}
static ir_node *mips_new_spill(ir_node *const value, ir_node *const after)
{
ir_mode *const mode = get_irn_mode(value);
if (be_mode_needs_gp_reg(mode)) {
ir_node *const block = get_block(after);
ir_graph *const irg = get_irn_irg(after);
ir_node *const nomem = get_irg_no_mem(irg);
ir_node *const frame = get_irg_frame(irg);
ir_node *const store = new_bd_mips_sw(NULL, block, nomem, frame, value, NULL, 0);
sched_add_after(after, store);
return store;
}
panic("TODO");
}
static ir_node *mips_new_reload(ir_node *const value, ir_node *const spill, ir_node *const before)
{
ir_mode *const mode = get_irn_mode(value);
if (be_mode_needs_gp_reg(mode)) {
ir_node *const block = get_block(before);
ir_graph *const irg = get_irn_irg(before);
ir_node *const frame = get_irg_frame(irg);
ir_node *const load = new_bd_mips_lw(NULL, block, spill, frame, NULL, 0);
sched_add_before(before, load);
return be_new_Proj(load, pn_mips_lw_res);
}
panic("TODO");
}
static regalloc_if_t const mips_regalloc_if = {
.spill_cost = 7,
.reload_cost = 5,
.new_spill = mips_new_spill,
.new_reload = mips_new_reload,
};
static void mips_collect_frame_entity_nodes(ir_node *const node, void *const data)
{
be_fec_env_t *const env = (be_fec_env_t*)data;
if (is_mips_lw(node)) {
ir_node *const base = get_irn_n(node, n_mips_lw_base);
ir_graph *const irg = get_irn_irg(node);
ir_node *const frame = get_irg_frame(irg);
if (base == frame) {
mips_immediate_attr_t const *const attr = get_mips_immediate_attr_const(node);
if (!attr->ent) {
unsigned const size = MIPS_MACHINE_SIZE / 8; // TODO
unsigned const po2align = log2_floor(size);
be_load_needs_frame_entity(env, node, size, po2align);
}
}
}
}
static void mips_set_frame_entity(ir_node *const node, ir_entity *const entity, unsigned const size, unsigned const po2align)
{
(void)size, (void)po2align;
mips_immediate_attr_t *const imm = get_mips_immediate_attr(node);
imm->ent = entity;
}
static void mips_assign_spill_slots(ir_graph *const irg)
{
be_fec_env_t *const fec_env = be_new_frame_entity_coalescer(irg);
irg_walk_graph(irg, NULL, mips_collect_frame_entity_nodes, fec_env);
be_assign_entities(fec_env, mips_set_frame_entity, true);
be_free_frame_entity_coalescer(fec_env);
}
static ir_node *mips_new_IncSP(ir_node *const block, ir_node *const sp, int const offset, unsigned const align)
{
return be_new_IncSP(&mips_registers[REG_SP], block, sp, offset, align);
}
static void mips_introduce_prologue(ir_graph *const irg, unsigned const size)
{
ir_node *const start = get_irg_start(irg);
ir_node *const block = get_nodes_block(start);
ir_node *const start_sp = be_get_Start_proj(irg, &mips_registers[REG_SP]);
ir_node *const inc_sp = mips_new_IncSP(block, start_sp, size, 0);
sched_add_after(start, inc_sp);
edges_reroute_except(start_sp, inc_sp, inc_sp);
}
static void mips_introduce_epilogue(ir_node *const ret, unsigned const size)
{
ir_node *const block = get_nodes_block(ret);
ir_node *const ret_sp = get_irn_n(ret, n_mips_ret_stack);
ir_node *const inc_sp = mips_new_IncSP(block, ret_sp, -(int)size, 0);
sched_add_before(ret, inc_sp);
set_irn_n(ret, n_mips_ret_stack, inc_sp);
}
static void mips_introduce_prologue_epilogue(ir_graph *const irg)
{
ir_type *const frame = get_irg_frame_type(irg);
unsigned const size = get_type_size(frame);
if (size == 0)
return;
foreach_irn_in(get_irg_end_block(irg), i, ret) {
assert(is_mips_ret(ret));
mips_introduce_epilogue(ret, size);
}
mips_introduce_prologue(irg, size);
}
static void mips_sp_sim(ir_node *const node, stack_pointer_state_t *const state)
{
if (is_mips_irn(node)) {
switch ((mips_opcodes)get_mips_irn_opcode(node)) {
case iro_mips_addiu:
case iro_mips_lb:
case iro_mips_lbu:
case iro_mips_lh:
case iro_mips_lhu:
case iro_mips_lw:
case iro_mips_sb:
case iro_mips_sh:
case iro_mips_sw: {
mips_immediate_attr_t *const imm = get_mips_immediate_attr(node);
ir_entity *const ent = imm->ent;
if (ent && is_frame_type(get_entity_owner(ent))) {
imm->ent = NULL;
imm->val += state->offset + get_entity_offset(ent);
}
break;
}
default:
break;
}
}
}
static void mips_generate_code(FILE *const output, char const *const cup_name)
{
be_begin(output, cup_name);
unsigned *const sp_is_non_ssa = rbitset_alloca(N_MIPS_REGISTERS);
rbitset_set(sp_is_non_ssa, REG_SP);
foreach_irp_irg(i, irg) {
if (!be_step_first(irg))
continue;
be_irg_t *const birg = be_birg_from_irg(irg);
birg->non_ssa_regs = sp_is_non_ssa;
mips_select_instructions(irg);
be_step_schedule(irg);
be_step_regalloc(irg, &mips_regalloc_if);
mips_assign_spill_slots(irg);
ir_type *const frame = get_irg_frame_type(irg);
be_sort_frame_entities(frame, true);
be_layout_frame_type(frame, 0, 0);
mips_introduce_prologue_epilogue(irg);
be_fix_stack_nodes(irg, &mips_registers[REG_SP]);
birg->non_ssa_regs = NULL;
be_sim_stack_pointer(irg, 0, 3, &mips_sp_sim);
mips_emit_function(irg);
be_step_last(irg);
}
be_finish();
}
static void mips_lower64(void)
{
ir_mode *const word_unsigned = mips_reg_classes[CLASS_mips_gp].mode;
ir_mode *const word_signed = find_signed_mode(word_unsigned);
lwrdw_param_t lower_dw_params = {
.create_intrinsic = NULL, // TODO
.word_unsigned = word_unsigned,
.word_signed = word_signed,
.doubleword_size = 64,
.big_endian = be_is_big_endian(),
};
ir_prepare_dw_lowering(&lower_dw_params);
ir_lower_dw_ops();
}
static void mips_lower_for_target(void)
{
mips_lower64();
}
static unsigned mips_get_op_estimated_cost(ir_node const *const node)
{
(void)node; // TODO
return 1;
}
static arch_isa_if_t const mips_isa_if = {
.n_registers = N_MIPS_REGISTERS,
.registers = mips_registers,
.n_register_classes = N_MIPS_CLASSES,
.register_classes = mips_reg_classes,
.init = mips_init,
.finish = mips_finish,
.get_params = mips_get_libfirm_params,
.generate_code = mips_generate_code,
.lower_for_target = mips_lower_for_target,
.is_valid_clobber = NULL, // TODO
.get_op_estimated_cost = mips_get_op_estimated_cost,
};
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_mips)
void be_init_arch_mips(void)
{
be_register_isa_if("mips", &mips_isa_if);
}
/*
* This file is part of libFirm.
* Copyright (C) 2017 University of Karlsruhe.
*/
#ifndef FIRM_BE_MIPS_MIPS_BEARCH_T_H
#define FIRM_BE_MIPS_MIPS_BEARCH_T_H
#define MIPS_MACHINE_SIZE 32
#endif
/*
* This file is part of libFirm.
* Copyright (C) 2017 University of Karlsruhe.
*/
#include "mips_cconv.h"
#include "betranshlp.h"
#include "gen_mips_regalloc_if.h"
#include "mips_bearch_t.h"
#include "util.h"
static unsigned const regs_param_gp[] = {
REG_A0,
REG_A1,
REG_A2,
REG_A3,
};
static unsigned const regs_result_gp[] = {
REG_V0,
REG_V1,
};
void mips_determine_calling_convention(mips_calling_convention_t *const cconv, ir_type *const fun_type)
{
/* Handle parameters. */
mips_reg_or_slot_t *params;
size_t const n_params = get_method_n_params(fun_type);
if (n_params != 0) {
params = XMALLOCNZ(mips_reg_or_slot_t, n_params);
size_t gp_param = 0;
for (size_t i = 0; i != n_params; ++i) {
ir_type *const param_type = get_method_param_type(fun_type, i);
ir_mode *const param_mode = get_type_mode(param_type);
if (mode_is_float(param_mode)) {
panic("TODO");
} else {
if (param_type->flags & tf_lowered_dw && gp_param % 2 != 0)
++gp_param;
if (gp_param < ARRAY_SIZE(regs_param_gp))
params[i].reg = &mips_registers[regs_param_gp[gp_param]];
params[i].offset = gp_param * (MIPS_MACHINE_SIZE / 8);
++gp_param;
}
}
} else {
params = 0;
}
cconv->parameters = params;
/* Handle results. */
mips_reg_or_slot_t *results;
size_t const n_results = get_method_n_ress(fun_type);
if (n_results != 0) {
results = XMALLOCNZ(mips_reg_or_slot_t, n_results);
size_t gp_res = 0;
for (size_t i = 0; i != n_results; ++i) {
ir_type *const res_type = get_method_res_type(fun_type, i);
ir_mode *const res_mode = get_type_mode(res_type);
if (mode_is_float(res_mode)) {
panic("TODO");
} else {
if (gp_res == ARRAY_SIZE(regs_result_gp))
panic("too many gp results");
results[i].reg = &mips_registers[regs_result_gp[gp_res++]];
}
}
} else {
results = 0;
}
cconv->results = results;
}
void mips_layout_parameter_entities(mips_calling_convention_t *const cconv, ir_graph *const irg)
{
ir_entity **const param_map = be_collect_parameter_entities(irg);
ir_type *const frame_type = get_irg_frame_type(irg);
ir_entity *const fun_ent = get_irg_entity(irg);
ir_type *const fun_type = get_entity_type(fun_ent);
size_t const n_params = get_method_n_params(fun_type);
for (size_t i = 0; i != n_params; ++i) {
mips_reg_or_slot_t *const param = &cconv->parameters[i];
ir_type *const param_type = get_method_param_type(fun_type, i);
if (!is_atomic_type(param_type))
panic("unhandled parameter type");
ir_entity *param_ent = param_map[i];
if (!param->reg) {
if (!param_ent)
param_ent = new_parameter_entity(frame_type, i, param_type);
assert(get_entity_offset(param_ent) == INVALID_OFFSET);
set_entity_offset(param_ent, param->offset);
}
param->entity = param_ent;
}
free(param_map);
}
void mips_free_calling_convention(mips_calling_convention_t *const cconv)
{
free(cconv->parameters);