Commit f833aa25 authored by Andreas Fried's avatar Andreas Fried Committed by Christoph Mallon
Browse files

Implement vararg handling in a more portable way (for now only for IA32).

This adds two builtin kinds, ir_bk_va_start and ir_bk_va_arg. va_arg Builtins
are lowered in the lower_builtins pass with a function provided by the backend,
whereby most architectures can use be_default_lower_va_args. va_start Builtins
are lowered in the backend.

Note: This commit breaks all backends except for IA32. SPARC and AMD64 are
implemented in later commits. The ARM backend previously miscompiled variadic
functions, and will now throw a proper error.
parent 7ce42e19
......@@ -78,6 +78,20 @@ typedef enum {
DW_LANG_Go = 0x0016,
} dwarf_source_language;
typedef void(*lower_func)(ir_node*);
/**
* This structure holds the information on how the backend implements
* variadic functions.
*/
typedef struct vararg_params {
/** Which type is to be used for va_list. If this is NULL, the
* backend does not implement variadic functions. */
ir_type *va_list_type;
/** The function to lower a call to the va_arg macro. */
lower_func lower_va_arg;
} vararg_params;
/**
* This structure contains parameters that should be
* propagated to the libFirm parameter set.
......@@ -135,6 +149,9 @@ typedef struct backend_params {
/** Semantic on float->int conversion overflow. */
float_int_conversion_overflow_style_t float_int_overflow;
/** How this backend implements variadic functions. */
vararg_params vararg;
} backend_params;
/**
......
......@@ -293,7 +293,9 @@ typedef enum ir_builtin_kind {
ir_bk_compare_swap, /**< compare exchange (aka. compare and swap) */
ir_bk_may_alias, /**< replaced by 0 if args cannot alias,
1 otherwise */
ir_bk_last = ir_bk_may_alias,
ir_bk_va_start, /**< va_start from <stdarg.h> */
ir_bk_va_arg, /**< va_arg from <stdarg.h> */
ir_bk_last = ir_bk_va_arg,
} ir_builtin_kind;
/**
......
......@@ -267,6 +267,7 @@ static void free_mark_proj(ir_node *node, unsigned n, pset *set)
case iro_Start:
case iro_Alloc:
case iro_Load:
case iro_Builtin:
/* nothing: operations are handled in free_ana_walker() */
break;
......
......@@ -757,7 +757,11 @@ static backend_params amd64_backend_params = {
.type_unsigned_long_long = NULL, /* will be set later */
.type_long_double = NULL, /* will be set later */
.stack_param_align = 8,
.float_int_overflow = ir_overflow_indefinite
.float_int_overflow = ir_overflow_indefinite,
.vararg = {
.va_list_type = NULL,
.lower_va_arg = NULL,
},
};
static const backend_params *amd64_get_backend_params(void) {
......
......@@ -1378,6 +1378,8 @@ static ir_node *gen_Builtin(ir_node *node)
case ir_bk_saturating_increment:
case ir_bk_compare_swap:
case ir_bk_may_alias:
case ir_bk_va_start:
case ir_bk_va_arg:
break;
}
panic("Builtin %s not implemented", get_builtin_kind_name(kind));
......@@ -1413,6 +1415,8 @@ static ir_node *gen_Proj_Builtin(ir_node *proj)
case ir_bk_saturating_increment:
case ir_bk_compare_swap:
case ir_bk_may_alias:
case ir_bk_va_start:
case ir_bk_va_arg:
break;
}
panic("Builtin %s not implemented", get_builtin_kind_name(kind));
......
......@@ -276,6 +276,10 @@ static backend_params arm_backend_params = {
.type_long_double = NULL,
.stack_param_align = 4,
.float_int_overflow = ir_overflow_min_max,
.vararg = {
.va_list_type = NULL,
.lower_va_arg = NULL,
},
};
static void arm_init_backend_params(void)
......
/*
* This file is part of libFirm.
* Copyright (C) 2012 University of Karlsruhe.
*/
/**
* @file
* @brief Default (pointer-based) implementation of variadic functions
* @author Andreas Fried
*/
#include "bevarargs.h"
#include "be.h"
#include "bitfiddle.h"
#include "ircons.h"
#include "irgmod.h"
#include "irnode.h"
#include "util.h"
void be_default_lower_va_arg(ir_node *node)
{
ir_node *block = get_nodes_block(node);
dbg_info *dbgi = get_irn_dbg_info(node);
ir_graph *irg = get_irn_irg(node);
ir_type *const aptype = get_method_res_type(get_Builtin_type(node), 0);
ir_node *const ap = get_irn_n(node, 1);
ir_node *const node_mem = get_Builtin_mem(node);
ir_mode *apmode = get_type_mode(aptype);
ir_node *res;
ir_node *new_mem;
if (apmode != NULL) {
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);
} else {
// aptype has no associated mode, so it is represented
// as a pointer.
apmode = mode_P;
res = ap;
new_mem = node_mem;
}
const backend_params *be_params = be_get_backend_param();
unsigned round_up = round_up2(get_type_size_bytes(aptype), be_params->stack_param_align);
ir_node *const diff_const = new_r_Const_long(irg, mode_Iu, round_up);
ir_node *const new_ap = new_rd_Add(dbgi, block, ap, diff_const, mode_P);
ir_node *const in[] = { new_mem, res, new_ap };
turn_into_tuple(node, ARRAY_SIZE(in), in);
}
/*
* This file is part of libFirm.
* Copyright (C) 2015 University of Karlsruhe.
*/
/**
* @file
* @brief Default (pointer-based) implementation of variadic functions
* @author Andreas Fried
*/
#ifndef FIRM_BE_BEVARARGS_H
#define FIRM_BE_BEVARARGS_H
#include "firm_types.h"
/**
* Default implementation to lower a va_arg node.
*
* This implementation assumes that all arguments are stored on the
* stack and that the va_list value points to the next variadic
* argument.
*
* @param node A Builtin node with kind ir_bk_va_arg to be lowered
*/
void be_default_lower_va_arg(ir_node *node);
#endif
......@@ -18,6 +18,7 @@
#include "bespillslots.h"
#include "bestack.h"
#include "beutil.h"
#include "bevarargs.h"
#include "gen_ia32_regalloc_if.h"
#include "ia32_architecture.h"
#include "ia32_emitter.h"
......@@ -29,6 +30,7 @@
#include "ia32_x87.h"
#include "ident_t.h"
#include "instrument.h"
#include "ircons.h"
#include "irgmod.h"
#include "irgopt.h"
#include "irgwalk.h"
......@@ -1398,6 +1400,10 @@ static backend_params ia32_backend_params = {
.type_long_double = NULL, /* will be set later */
.stack_param_align = 4,
.float_int_overflow = ir_overflow_indefinite,
.vararg = {
.va_list_type = NULL, /* will be set later */
.lower_va_arg = be_default_lower_va_arg,
},
};
/**
......@@ -1437,6 +1443,10 @@ static void ia32_init(void)
ia32_backend_params.type_long_long = type_long_long;
ia32_backend_params.type_unsigned_long_long = type_unsigned_long_long;
// va_list is a void pointer
ir_type *type_va_list = new_type_pointer(new_type_primitive(mode_ANY));
ia32_backend_params.vararg.va_list_type = type_va_list;
if (ia32_cg_config.use_sse2 || ia32_cg_config.use_softfloat) {
ia32_backend_params.mode_float_arithmetic = NULL;
ia32_backend_params.type_long_double = NULL;
......@@ -1557,6 +1567,7 @@ static void ia32_lower_for_target(void)
supported[s++] = ir_bk_outport;
supported[s++] = ir_bk_inport;
supported[s++] = ir_bk_saturating_increment;
supported[s++] = ir_bk_va_start;
if (ia32_cg_config.use_popcnt)
supported[s++] = ir_bk_popcount;
if (ia32_cg_config.use_cmpxchg)
......
......@@ -57,6 +57,8 @@ static pmap *node_to_stack;
static be_stackorder_t *stackorder;
static ir_heights_t *heights;
static x86_immediate_kind_t lconst_imm_kind;
static ir_entity *va_start_entity;
static ir_node *initial_va_list;
/** we don't have a concept of aliasing registers, so enumerate them
* manually for the asm nodes. */
......@@ -5520,6 +5522,28 @@ static ir_node *gen_compare_swap(ir_node *node)
return new_node;
}
static ir_node *gen_va_start(ir_node *node)
{
if (initial_va_list == NULL) {
dbg_info *dbgi = get_irn_dbg_info(node);
ir_graph *irg = get_irn_irg(node);
ir_node *block = get_irg_start_block(irg);
ir_node *frame = get_irg_frame(irg);
ir_node *ap = new_bd_ia32_Lea(dbgi, block, frame, noreg_GP);
set_ia32_frame_use(ap, IA32_FRAME_USE_AUTO);
ia32_attr_t *const attr = get_ia32_attr(ap);
attr->am_imm = (x86_imm32_t){
.kind = X86_IMM_FRAMEOFFSET,
.entity = va_start_entity,
};
initial_va_list = ap;
}
return initial_va_list;
}
/**
* Transform Builtin node.
*/
......@@ -5558,7 +5582,10 @@ static ir_node *gen_Builtin(ir_node *node)
return gen_saturating_increment(node);
case ir_bk_compare_swap:
return gen_compare_swap(node);
case ir_bk_va_start:
return gen_va_start(node);
case ir_bk_may_alias:
case ir_bk_va_arg:
break;
}
panic("Builtin %s not implemented", get_builtin_kind_name(kind));
......@@ -5606,7 +5633,18 @@ static ir_node *gen_Proj_Builtin(ir_node *proj)
assert(get_Proj_num(proj) == pn_Builtin_max+1);
return be_new_Proj(new_node, pn_ia32_CmpXChgMem_res);
}
case ir_bk_va_start:
switch(get_Proj_num(proj)) {
case pn_Builtin_M: {
ir_node *mem = get_Builtin_mem(node);
return be_transform_node(mem);
}
case pn_Builtin_max + 1:
return new_node;
}
break;
case ir_bk_may_alias:
case ir_bk_va_arg:
break;
}
panic("Builtin %s not implemented", get_builtin_kind_name(kind));
......@@ -5771,6 +5809,7 @@ static ir_type *ia32_get_between_type(bool omit_fp)
return omit_fp ? omit_fp_between_type : between_type;
}
static void ia32_create_stacklayout(ir_graph *irg, const x86_cconv_t *cconv)
{
/* construct argument type */
......@@ -5780,7 +5819,7 @@ static void ia32_create_stacklayout(ir_graph *irg, const x86_cconv_t *cconv)
ir_type *const frame_type = get_irg_frame_type(irg);
size_t const n_params = cconv->n_parameters;
ir_entity **const param_map = ALLOCANZ(ir_entity*, n_params);
ir_entity *va_start_entity = NULL;
for (size_t f = get_compound_n_members(frame_type); f-- > 0; ) {
ir_entity *member = get_compound_member(frame_type, f);
if (!is_parameter_entity(member))
......@@ -5788,13 +5827,6 @@ static void ia32_create_stacklayout(ir_graph *irg, const x86_cconv_t *cconv)
set_entity_owner(member, arg_type);
size_t num = get_entity_parameter_number(member);
if (num == IR_VA_START_PARAMETER_NUMBER) {
if (va_start_entity != NULL)
panic("multiple va_start entities found (%+F,%+F)",
va_start_entity, member);
va_start_entity = member;
continue;
}
assert(num < n_params);
if (param_map[num] != NULL)
panic("multiple entities for parameter %u in %+F found", f, irg);
......@@ -5813,9 +5845,15 @@ static void ia32_create_stacklayout(ir_graph *irg, const x86_cconv_t *cconv)
param->entity = entity;
set_entity_offset(param->entity, param->offset);
}
if (va_start_entity != NULL) {
ir_type *const function_type = get_entity_type(entity);
if (is_method_variadic(function_type)) {
ir_type *unknown = get_unknown_type();
va_start_entity = new_parameter_entity(arg_type, IR_VA_START_PARAMETER_NUMBER, unknown);
set_entity_offset(va_start_entity, cconv->callframe_size);
}
set_type_size_bytes(arg_type, cconv->callframe_size);
be_stack_layout_t *const layout = be_get_irg_stack_layout(irg);
......@@ -5885,6 +5923,7 @@ void ia32_transform_graph(ir_graph *irg)
x86_free_calling_convention(current_cconv);
pmap_destroy(node_to_stack);
node_to_stack = NULL;
initial_va_list = NULL;
}
void ia32_init_transform(void)
......
......@@ -535,6 +535,10 @@ static const backend_params *sparc_get_backend_params(void)
.type_long_double = NULL, /* will be set later */
.stack_param_align = 4,
.float_int_overflow = ir_overflow_min_max,
.vararg = {
.va_list_type = NULL,
.lower_va_arg = NULL,
},
};
ir_mode *mode_long_long
......
......@@ -2099,6 +2099,8 @@ static ir_node *gen_Builtin(ir_node *node)
case ir_bk_saturating_increment:
return gen_saturating_increment(node);
case ir_bk_may_alias:
case ir_bk_va_start:
case ir_bk_va_arg:
break;
}
panic("Builtin %s not implemented", get_builtin_kind_name(kind));
......@@ -2141,6 +2143,8 @@ static ir_node *gen_Proj_Builtin(ir_node *proj)
return be_new_Proj(new_pred, pn_sparc_Cas_res);
}
case ir_bk_may_alias:
case ir_bk_va_start:
case ir_bk_va_arg:
break;
}
panic("Builtin %s not implemented", get_builtin_kind_name(kind));
......
......@@ -570,6 +570,8 @@ const char *get_builtin_kind_name(ir_builtin_kind kind)
X(ir_bk_saturating_increment);
X(ir_bk_compare_swap);
X(ir_bk_may_alias);
X(ir_bk_va_start);
X(ir_bk_va_arg);
}
return "<unknown>";
#undef X
......@@ -826,6 +828,8 @@ int is_irn_const_memory(const ir_node *node)
case ir_bk_trap:
case ir_bk_debugbreak:
case ir_bk_compare_swap:
case ir_bk_va_start:
case ir_bk_va_arg:
return false;
case ir_bk_return_address:
case ir_bk_frame_address:
......
......@@ -12,6 +12,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include "adt/pmap.h"
#include "be.h"
#include "irnode_t.h"
#include "ircons_t.h"
#include "irgmod.h"
......@@ -42,6 +43,8 @@ static const char *get_builtin_name(ir_builtin_kind kind)
case ir_bk_saturating_increment:
case ir_bk_compare_swap:
case ir_bk_may_alias:
case ir_bk_va_start:
case ir_bk_va_arg:
break;
}
abort();
......@@ -145,6 +148,10 @@ changed:
*changed = true;
return;
case ir_bk_va_arg:
be_get_backend_param()->vararg.lower_va_arg(node);
return;
case ir_bk_trap:
case ir_bk_debugbreak:
case ir_bk_return_address:
......@@ -153,6 +160,7 @@ changed:
case ir_bk_outport:
case ir_bk_saturating_increment:
case ir_bk_compare_swap:
case ir_bk_va_start:
/* can't do anything about these, backend will probably fail now */
panic("cannot lower Builtin node of kind %+F", node);
}
......@@ -168,6 +176,7 @@ void lower_builtins(size_t n_exceptions, ir_builtin_kind *exceptions)
foreach_irp_irg(i, irg) {
bool changed = false;
assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
irg_walk_graph(irg, NULL, lower_builtin, &changed);
confirm_irg_properties(irg, changed ? IR_GRAPH_PROPERTIES_CONTROL_FLOW
: IR_GRAPH_PROPERTIES_ALL);
......
......@@ -2424,6 +2424,8 @@ static void lower_Builtin(ir_node *builtin, ir_mode *mode)
case ir_bk_return_address:
case ir_bk_saturating_increment:
case ir_bk_trap:
case ir_bk_va_start:
case ir_bk_va_arg:
/* Nothing to do/impossible to lower in a generic way */
return;
case ir_bk_bswap:
......
......@@ -360,13 +360,9 @@ static ir_type *lower_method_type(ir_type *mtp)
return res;
}
/**
* Adapts the method type of a Call.
*/
static bool lower_Call(ir_node *node)
static ir_type *lower_type_if_needed(ir_type *tp)
{
bool need_lower = false;
ir_type *tp = get_Call_type(node);
size_t const n_params = get_method_n_params(tp);
for (size_t p = 0; p < n_params; ++p) {
......@@ -388,11 +384,39 @@ static bool lower_Call(ir_node *node)
}
}
if (!need_lower)
if (need_lower) {
return lower_method_type(tp);
} else {
return tp;
}
}
/**
* Adapts the method type of a Call.
*/
static bool lower_Call(ir_node *node)
{
ir_type *tp = get_Call_type(node);
ir_type *lower_tp = lower_type_if_needed(tp);
if (lower_tp != tp) {
set_Call_type(node, lower_tp);
}
return true;
}
/**
* Adapts the method type of a va_arg Builtin.
*/
static bool lower_Builtin(ir_node *node)
{
if (get_Builtin_kind(node) != ir_bk_va_arg)
return false;
tp = lower_method_type(tp);
set_Call_type(node, tp);
ir_type *tp = get_Builtin_type(node);
ir_type *lower_tp = lower_type_if_needed(tp);
if (lower_tp != tp) {
set_Builtin_type(node, lower_tp);
}
return true;
}
......@@ -836,6 +860,7 @@ void lower_floating_point(void)
ir_clear_opcodes_generic_func();
ir_register_softloat_lower_function(op_Bitcast, lower_Bitcast);
ir_register_softloat_lower_function(op_Call, lower_Call);
ir_register_softloat_lower_function(op_Builtin, lower_Builtin);
ir_register_softloat_lower_function(op_Const, lower_Const);
ir_register_softloat_lower_function(op_Div, lower_Div_mode);
ir_register_softloat_lower_function(op_Load, lower_Load);
......
......@@ -409,6 +409,9 @@ next_no_change:
case ir_bk_inport:
case ir_bk_outport:
/* anything can happen when accessing periphery... */
case ir_bk_va_start:
case ir_bk_va_arg:
/* The va_list parameter might not be local to the function. */
return mtp_no_property;
}
break;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment