Commit 10790875 authored by Matthias Braun's avatar Matthias Braun
Browse files

sparc: add support for more asm constraints

parent f179d707
......@@ -169,25 +169,6 @@ static sparc_isa_t sparc_isa_template = {
SPARC_FPU_ARCH_FPU, /* FPU architecture */
};
static void init_asm_constraints(void)
{
asm_constraint_flags['r'] = ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER;
asm_constraint_flags['e'] = ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER;
asm_constraint_flags['f'] = ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER;
asm_constraint_flags['A'] = ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE;
asm_constraint_flags['I'] = ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE;
asm_constraint_flags['='] = ASM_CONSTRAINT_FLAG_MODIFIER_WRITE
| ASM_CONSTRAINT_FLAG_MODIFIER_NO_READ;
asm_constraint_flags['+'] = ASM_CONSTRAINT_FLAG_MODIFIER_READ
| ASM_CONSTRAINT_FLAG_MODIFIER_WRITE;
/* Note there are many more flags in gcc which we can't properly support
* at the moment. see gcc/config/sparc/constraints.md
* Not supported: 'f', 'e', 'c', 'd', 'b', 'h', 'D', 'J', 'K',
* 'L', 'M', 'N', 'O', 'G', 'H', 'Q', 'R', 'S', 'T', 'U', 'W', 'Y' */
}
/**
* rewrite unsigned->float conversion.
* Sparc has no instruction for this so instead we do the following:
......@@ -394,7 +375,7 @@ static void sparc_handle_intrinsics(void)
static void sparc_init(void)
{
init_asm_constraints();
sparc_init_asm_constraints();
sparc_register_init();
sparc_create_opcodes(&sparc_irn_ops);
sparc_cconv_init();
......@@ -517,12 +498,6 @@ static const backend_params *sparc_get_backend_params(void)
return &p;
}
static int sparc_is_valid_clobber(const char *clobber)
{
(void) clobber;
return 0;
}
/* fpu set architectures. */
static const lc_opt_enum_int_items_t sparc_fpu_items[] = {
{ "fpu", SPARC_FPU_ARCH_FPU },
......
......@@ -63,13 +63,9 @@ static void sparc_emit_indent(void)
be_emit_char(' ');
}
static void sparc_emit_immediate(ir_node const *const node)
static void sparc_emit_immediate(int32_t value, ir_entity *entity)
{
const sparc_attr_t *attr = get_sparc_attr_const(node);
ir_entity *entity = attr->immediate_value_entity;
if (entity == NULL) {
int32_t value = attr->immediate_value;
assert(sparc_is_value_imm_encodeable(value));
be_emit_irprintf("%d", value);
} else {
......@@ -79,8 +75,8 @@ static void sparc_emit_immediate(ir_node const *const node)
be_emit_cstring("%lo(");
}
be_gas_emit_entity(entity);
if (attr->immediate_value != 0) {
be_emit_irprintf("%+d", attr->immediate_value);
if (value != 0) {
be_emit_irprintf("%+d", value);
}
be_emit_char(')');
}
......@@ -108,18 +104,22 @@ static void sparc_emit_high_immediate(ir_node const *node)
}
}
static void sparc_emit_source_register(ir_node const *node, int const pos)
static void sparc_emit_register(const arch_register_t *const reg)
{
const arch_register_t *reg = arch_get_irn_register_in(node, pos);
be_emit_char('%');
be_emit_string(reg->name);
}
static void sparc_emit_source_register(ir_node const *node, int const pos)
{
const arch_register_t *reg = arch_get_irn_register_in(node, pos);
sparc_emit_register(reg);
}
static void sparc_emit_dest_register(ir_node const *const node, int const pos)
{
const arch_register_t *reg = arch_get_irn_register_out(node, pos);
be_emit_char('%');
be_emit_string(reg->name);
sparc_emit_register(reg);
}
/**
......@@ -144,7 +144,8 @@ static void sparc_emit_offset(const ir_node *node, int offset_node_pos)
} else if (attr->base.immediate_value != 0
|| attr->base.immediate_value_entity != NULL) {
be_emit_char('+');
sparc_emit_immediate(node);
sparc_emit_immediate(attr->base.immediate_value,
attr->base.immediate_value_entity);
}
}
......@@ -658,7 +659,9 @@ void sparc_emitf(ir_node const *const node, char const *fmt, ...)
goto unknown;
unsigned const pos = *fmt++ - '0';
if (imm && arch_get_irn_flags(node) & (arch_irn_flags_t)sparc_arch_irn_flag_immediate_form) {
sparc_emit_immediate(node);
const sparc_attr_t *const attr = get_sparc_attr_const(node);
sparc_emit_immediate(attr->immediate_value,
attr->immediate_value_entity);
} else {
sparc_emit_source_register(node, pos);
}
......@@ -707,6 +710,59 @@ static void emit_be_IncSP(const ir_node *irn)
sparc_emitf(irn, "%s %S0, %d, %D0", insn, offset);
}
static const char *emit_asm_operand(const ir_node *node, const char *s)
{
assert(*s == '%');
char c = *(++s);
/* parse modifiers */
if (c == '\0') {
ir_fprintf(stderr, "Warning: asm text (%+F) ends with %%\n", node);
be_emit_char('%');
return s;
} else if (c == '%') {
be_emit_char('%');
return s+1;
} else if (c < '0' || c > '9') {
ir_fprintf(stderr, "Warning: asm text (%+F) contains unknown modifier '%c' for asm op\n",
node, c);
return s+1;
}
/* parse number */
int num = 0;
int p = 0;
sscanf(s, "%d%n", &num, &p);
s += p;
const sparc_asm_attr_t *const attr = get_sparc_asm_attr_const(node);
const sparc_asm_operand_t *const operands = attr->operands;
if ((size_t)num > ARR_LEN(operands)) {
ir_fprintf(stderr,
"Error: Custom assembler references invalid input/output (%+F)\n",
node);
return s;
}
const sparc_asm_operand_t *const operand = &operands[num];
const arch_register_t *reg = NULL;
switch (operand->kind) {
case ASM_OPERAND_IMMEDIATE:
sparc_emit_immediate(operand->immediate_value,
operand->immediate_value_entity);
return s;
case ASM_OPERAND_INPUT_VALUE:
reg = arch_get_irn_register_in(node, operand->pos);
break;
case ASM_OPERAND_OUTPUT_VALUE:
reg = arch_get_irn_register_out(node, operand->pos);
break;
}
/* emit the register */
sparc_emit_register(reg);
return s;
}
static void emit_sparc_ASM(const ir_node *node)
{
be_emit_cstring("#APP\n");
......@@ -717,7 +773,13 @@ static void emit_sparc_ASM(const ir_node *node)
if (s[0] != '\t')
be_emit_char('\t');
be_emit_string(s);
while (*s != 0) {
if (*s == '%') {
s = emit_asm_operand(node, s);
} else {
be_emit_char(*s++);
}
}
be_emit_cstring("\n#NO_APP\n");
be_emit_write_line();
......
......@@ -12,6 +12,7 @@
#include "bearch_sparc_t.h"
#include "error.h"
#include "sparc_nodes_attr.h"
#include "gen_sparc_new_nodes.h"
#include "lower_dw.h"
#include "ircons_t.h"
......
......@@ -274,10 +274,12 @@ static void init_sparc_switch_jmp_attributes(ir_node *node,
}
}
static void init_sparc_asm_attributes(ir_node *node, ident *text)
static void init_sparc_asm_attributes(ir_node *node, ident *text,
const sparc_asm_operand_t *operands)
{
sparc_asm_attr_t *attr = get_sparc_asm_attr(node);
attr->text = text;
attr->text = text;
attr->operands = operands;
}
/**
......
......@@ -81,10 +81,24 @@ struct sparc_switch_jmp_attr_t {
ir_entity *table_entity;
};
typedef enum operand_kind_t {
ASM_OPERAND_INPUT_VALUE,
ASM_OPERAND_OUTPUT_VALUE,
ASM_OPERAND_IMMEDIATE
} operand_kind_t;
typedef struct sparc_asm_operand_t {
operand_kind_t kind;
unsigned pos;
int32_t immediate_value;
ir_entity *immediate_value_entity;
} sparc_asm_operand_t;
typedef struct sparc_asm_attr_t sparc_asm_attr_t;
struct sparc_asm_attr_t {
sparc_attr_t base;
ident *text;
sparc_attr_t base;
ident *text;
const sparc_asm_operand_t *operands;
};
enum n_sparc_Return {
......
......@@ -115,7 +115,7 @@ $default_copy_attr = "sparc_copy_attr";
sparc_fp_conv_attr_t => "\tinit_sparc_attributes(res, irn_flags_, in_reqs, n_res);\n".
"\tinit_sparc_fp_conv_attributes(res, src_mode, dest_mode);\n",
sparc_asm_attr_t => "\tinit_sparc_attributes(res, irn_flags_, in_reqs, n_res);\n".
"\tinit_sparc_asm_attributes(res, text);",
"\tinit_sparc_asm_attributes(res, text, operands);",
);
%compare_attr = (
......@@ -243,10 +243,9 @@ my %float_unop_constructors = (
ASM => {
state => "exc_pinned",
reg_req => { in => [ "none" ], out => [ "none" ] },
ins => [ "mem" ],
outs => [ "M" ],
attr => "ident *text",
arity => "variable",
out_arity => "variable",
attr => "ident *text, const sparc_asm_operand_t *operands",
attr_type => "sparc_asm_attr_t",
},
......
......@@ -220,6 +220,365 @@ static ir_node *skip_downconv(ir_node *node)
return node;
}
/**
* An assembler constraint.
*/
typedef struct constraint_t {
const arch_register_class_t *cls;
char all_registers_allowed;
char immediate_type;
int same_as;
} constraint_t;
static void parse_asm_constraints(constraint_t *const constraint,
ident *const constraint_text,
bool const is_output)
{
memset(constraint, 0, sizeof(constraint[0]));
constraint->same_as = -1;
char const *c = get_id_str(constraint_text);
if (*c == '\0') {
/* a memory constraint: no need to do anything in backend about it
* (the dependencies are already respected by the memory edge of
* the node) */
return;
}
char immediate_type = '\0';
arch_register_class_t const *cls = NULL;
bool all_registers_allowed = false;
int same_as = -1;
while (*c != 0) {
arch_register_class_t const *new_cls = NULL;
char new_imm = '\0';
switch (*c) {
/* Skip spaces, out/in-out marker */
case ' ':
case '\t':
case '\n':
case '=':
case '+':
case '&': break;
case '*':
++c;
break;
case '#':
while (*c != 0 && *c != ',')
++c;
break;
case 'r':
new_cls = &sparc_reg_classes[CLASS_sparc_gp];
all_registers_allowed = true;
break;
case 'e':
case 'f':
new_cls = &sparc_reg_classes[CLASS_sparc_fp];
all_registers_allowed = true;
break;
case 'A':
case 'I':
new_cls = &sparc_reg_classes[CLASS_sparc_gp];
new_imm = *c;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
if (is_output)
panic("can only specify same constraint on input");
int p;
sscanf(c, "%d%n", &same_as, &p);
if (same_as >= 0) {
c += p;
continue;
}
break;
}
default:
panic("unknown asm constraint '%c' found in (%+F)", *c, current_ir_graph);
}
if (new_cls) {
if (!cls) {
cls = new_cls;
} else if (cls != new_cls) {
panic("multiple register classes not supported");
}
}
if (new_imm != '\0') {
if (immediate_type == '\0') {
immediate_type = new_imm;
} else if (immediate_type != new_imm) {
panic("multiple immediate types not supported");
}
}
++c;
}
if (same_as >= 0) {
if (cls != NULL)
panic("same as and register constraint not supported");
if (immediate_type != '\0')
panic("same as and immediate constraint not supported");
}
if (!cls && same_as < 0)
panic("no constraint specified for assembler input");
constraint->same_as = same_as;
constraint->cls = cls;
constraint->all_registers_allowed = all_registers_allowed;
constraint->immediate_type = immediate_type;
}
static const arch_register_t *find_register(const char *name)
{
for (size_t i = 0; i < N_SPARC_REGISTERS; ++i) {
const arch_register_t *const reg = &sparc_registers[i];
if (strcmp(reg->name, name) == 0)
return reg;
}
return NULL;
}
static arch_register_req_t const *make_register_req(ir_graph *const irg,
constraint_t const *const c, int const n_outs,
arch_register_req_t const **const out_reqs, int const pos)
{
int const same_as = c->same_as;
if (same_as >= 0) {
if (same_as >= n_outs)
panic("invalid output number in same_as constraint");
struct obstack *const obst = get_irg_obstack(irg);
arch_register_req_t *const req = OALLOC(obst, arch_register_req_t);
arch_register_req_t const *const other = out_reqs[same_as];
*req = *other;
req->type |= arch_register_req_type_should_be_same;
req->other_same = 1U << pos;
/* Switch constraints. This is because in firm we have same_as
* constraints on the output constraints while in the gcc asm syntax
* they are specified on the input constraints. */
out_reqs[same_as] = req;
return other;
}
return c->cls->class_req;
}
void sparc_init_asm_constraints(void)
{
static unsigned char const register_flags[] = {
'r', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
for (size_t i = 0; i < ARRAY_SIZE(register_flags); ++i) {
unsigned char const c = register_flags[i];
asm_constraint_flags[c] = ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER;
}
asm_constraint_flags['A'] = ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE;
asm_constraint_flags['I'] = ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE;
/* Note there are many more flags in gcc which we can't properly support
* at the moment. see gcc/config/sparc/constraints.md */
}
int sparc_is_valid_clobber(const char *clobber)
{
return strcmp(clobber, "memory") == 0 || strcmp(clobber, "cc") == 0;
}
static ir_node *gen_ASM(ir_node *node)
{
int n_inputs = get_ASM_n_inputs(node);
size_t n_clobbers = 0;
ident **clobbers = get_ASM_clobbers(node);
ir_graph *irg = get_irn_irg(node);
//unsigned clobber_bits[BITSET_SIZE_ELEMS(N_SPARC_REGISTERS)];
for (size_t c = 0; c < get_ASM_n_clobbers(node); ++c) {
const char *const clobber = get_id_str(clobbers[c]);
if (strcmp(clobber, "memory") == 0)
continue;
if (strcmp(clobber, "cc") == 0) {
n_clobbers += 2;
continue;
}
const arch_register_t *reg = find_register(clobber);
if (reg == NULL)
panic("invalid clobber in sparc asm");
#if 0
rbitset_set(clobber_bits, reg->global_index);
++n_clobbers;
#else
panic("clobbers not correctly supported yet");
#endif
}
size_t n_out_constraints = get_ASM_n_output_constraints(node);
size_t n_outs = n_out_constraints + n_clobbers;
const ir_asm_constraint *in_constraints = get_ASM_input_constraints(node);
const ir_asm_constraint *out_constraints = get_ASM_output_constraints(node);
/* determine number of operands */
unsigned n_operands = 0;
for (size_t out_idx = 0; out_idx < n_out_constraints; ++out_idx) {
const ir_asm_constraint *constraint = &out_constraints[out_idx];
if (constraint->pos+1 > n_operands)
n_operands = constraint->pos+1;
}
for (int i = 0; i < n_inputs; ++i) {
const ir_asm_constraint *constraint = &in_constraints[i];
if (constraint->pos+1 > n_operands)
n_operands = constraint->pos+1;
}
struct obstack *const obst = get_irg_obstack(irg);
sparc_asm_operand_t *const operands
= NEW_ARR_DZ(sparc_asm_operand_t, obst, n_operands);
/* construct output constraints */
size_t out_size = n_outs + 1;
const arch_register_req_t **out_reg_reqs
= OALLOCN(obst, const arch_register_req_t*, out_size);
size_t out_idx;
for (out_idx = 0; out_idx < n_out_constraints; ++out_idx) {
const ir_asm_constraint *constraint = &out_constraints[out_idx];
unsigned pos = constraint->pos;
constraint_t parsed_constraint;
parse_asm_constraints(&parsed_constraint, constraint->constraint, true);
assert(parsed_constraint.immediate_type == 0);
arch_register_req_t const *const req
= make_register_req(irg, &parsed_constraint, n_out_constraints,
out_reg_reqs, out_idx);
out_reg_reqs[out_idx] = req;
/* TODO: adjust register_req for clobbers */
sparc_asm_operand_t *const operand = &operands[pos];
operand->kind = ASM_OPERAND_OUTPUT_VALUE;
operand->pos = out_idx;
}
/* inputs + input constraints */
int max_ins = n_inputs+1;
ir_node **in = ALLOCANZ(ir_node*, max_ins);
const arch_register_req_t **in_reg_reqs
= OALLOCN(obst, const arch_register_req_t*, max_ins);
int n_ins = 0;
for (int i = 0; i < n_inputs; ++i) {
ir_node *pred = get_ASM_input(node, i);
const ir_asm_constraint *constraint = &in_constraints[i];
unsigned pos = constraint->pos;
constraint_t parsed_constraint;
parse_asm_constraints(&parsed_constraint, constraint->constraint, false);
sparc_asm_operand_t *const operand = &operands[pos];
/* try to use an immediate value */
char imm_type = parsed_constraint.immediate_type;
if (imm_type == 'I') {
if (is_imm_encodeable(pred)) {
operand->kind = ASM_OPERAND_IMMEDIATE;
operand->immediate_value = get_tarval_long(get_Const_tarval(pred));
continue;
}
} else if (imm_type == 'A') {
/* TODO: match Add(SymConst,Const), ... */
if (is_SymConst(pred)) {
operand->kind = ASM_OPERAND_IMMEDIATE;
operand->immediate_value_entity = get_SymConst_entity(pred);
continue;
} else if (is_Const(pred)) {
operand->kind = ASM_OPERAND_IMMEDIATE;
operand->immediate_value = get_tarval_long(get_Const_tarval(pred));
continue;
}
}
arch_register_req_t const *const req = make_register_req(irg, &parsed_constraint, n_out_constraints, out_reg_reqs, i);
in_reg_reqs[i] = req;
int op_pos = n_ins++;
ir_node *new_pred = be_transform_node(pred);
in[op_pos] = new_pred;
operand->kind = ASM_OPERAND_INPUT_VALUE;
operand->pos = op_pos;
}
int mem_pos = n_ins++;
ir_node *mem = get_ASM_mem(node);
in[mem_pos] = be_transform_node(mem);
in_reg_reqs[mem_pos] = arch_no_register_req;
/* parse clobbers */
for (size_t c = 0; c < get_ASM_n_clobbers(node); ++c) {
const char *const clobber = get_id_str(clobbers[c]);
if (strcmp(clobber, "memory") == 0)
continue;
if (strcmp(clobber, "cc") == 0) {
const arch_register_t *flags_reg
= &sparc_registers[REG_FLAGS];
out_reg_reqs[out_idx++] = flags_reg->single_req;
const arch_register_t *fpflags_reg
= &sparc_registers[REG_FPFLAGS];
out_reg_reqs[out_idx++] = fpflags_reg->single_req;
continue;
}
const arch_register_t *reg = find_register(clobber);
assert(reg != NULL); /* otherwise we had a panic earlier */
out_reg_reqs[out_idx++] = reg->single_req;
}
/* append none register requirement for the memory output */
if (n_outs+1 >= out_size) {
out_size = n_outs + 1;
const arch_register_req_t **new_out_reg_reqs
= OALLOCN(obst, const arch_register_req_t*, out_size);
memcpy(new_out_reg_reqs, out_reg_reqs,
n_outs * sizeof(new_out_reg_reqs[0]));
out_reg_reqs = new_out_reg_reqs;
}
/* add a new (dummy) output which occupies the register */