Commit 1ffc842f authored by Matthias Braun's avatar Matthias Braun

Rework target initialization and query

- Moves machine triple handling code form cparser into libfirm
- Create new APIs to set the target and query information about it
    - Move backend_params into the new target API
    - Backends initialize ir_target instead of backend_params now
- Add new API to get information about the target platform:
    - Mangle a name for the target platform (and remove compilerlib
      mangling callback)
    - Can query size and alignment of basic C types for the platform
- Move some constant target information into arch_isa_if_t
  (we move it to target_info_t later when we realize it needs to be
   dynamic)
- Redo backend initialization. Examples:

Simple case: Initialize for host:

    ir_init();

Complex case: cross-compile to sparc with PIC enabled:

    ir_init_library();
    ir_target_set("sparc-leon-linux-gnu");
    ir_target_option("pic");
    ir_target_init();
parent 845e655d
......@@ -77,106 +77,6 @@ typedef enum {
DW_LANG_Go = 0x0016,
} dwarf_source_language;
/**
* This structure contains parameters that should be
* propagated to the libFirm parameter set.
*/
typedef struct backend_params {
/* Info whether this backend is unfinished. */
char const *experimental;
/** the backend uses big-endian byte ordering if set, else little endian */
unsigned byte_order_big_endian:1;
/** 1 if backend supports generation of position independent code (PIC) */
unsigned pic_supported:1;
/** unaligned memory accesses are not supported natively (but the backend
* may break the access up into several smaller ones) */
unsigned unaligned_memaccess_supported:1;
/** Whether thread local storage is supported. */
unsigned thread_local_storage_supported:1;
/**
* Shifts on this architecture only read some bits of the shift value.
* For example on x86 for every mode with less than 32bits only 5 bits of
* the shift value are read resulting in a modulo shift value of 32.
* On an architecture without modulo_shift this value is 0.
*/
unsigned modulo_shift;
/** Backend settings for if-conversion. */
arch_allow_ifconv_func allow_ifconv;
/** size of machine word in bits. This is usually the size of the general
* purpose integer/address registers. */
unsigned machine_size;
/**
* some backends like x87 can only do arithmetic in a specific float
* mode (load/store are still done in the "normal" float/double modes).
*/
ir_mode *mode_float_arithmetic;
/**
* type used for long double or NULL if none available.
*/
ir_type *type_long_double;
/** Semantic on float->int conversion overflow. */
float_int_conversion_overflow_style_t float_int_overflow;
/** 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;
} backend_params;
/**
* Parse one backend argument. This is intended to provide commandline options
* to various backend parameters that might be changing.
* Returns -1 if 'help' was found, 0 if the argument could not be parsed,
* 1 if the option could be set.
*/
FIRM_API int be_parse_arg(const char *arg);
/**
* Returns 1 if the backend uses big-endian byte ordering
* and 0 for little-endian.
*/
FIRM_API int be_is_big_endian(void);
/**
* Returns size of machine words. This is usually the size
* of the general purpose integer registers.
*/
FIRM_API unsigned be_get_machine_size(void);
/**
* Returns supported float arithmetic mode or NULL if mode_D and mode_F
* are supported natively.
* Some backends like x87 can only do arithmetic in a specific float
* mode (load/store are still done in the "normal" float/double modes).
*/
FIRM_API ir_mode *be_get_mode_float_arithmetic(void);
/** Returns type used for long double or NULL if none available. */
FIRM_API ir_type *be_get_type_long_double(void);
/** Returns the backend behaviour on float to integer conversion overflow. */
FIRM_API float_int_conversion_overflow_style_t be_get_float_int_overflow(void);
/**
* Initialize the backend for the selected isa. This initializes mode_P and
* should be called after all options are set with be_parse_arg().
*/
FIRM_API void be_initialize(void);
/**
* Returns the backend configuration parameter.
*
* @return libFirm configuration parameters for the selected
* backend
*/
FIRM_API const backend_params *be_get_backend_param(void);
/**
* Lowers current program for the target architecture.
* This must be run once before using be_main. The idea here is that the backend
......
......@@ -78,10 +78,9 @@
#include "heights.h"
#include "ident.h"
#include "ircgopt.h"
#include "irconsconfirm.h"
#include "ircons.h"
#include "irconsconfirm.h"
#include "irdom.h"
#include "vrp.h"
#include "irdump.h"
#include "iredgekinds.h"
#include "iredges.h"
......@@ -103,8 +102,10 @@
#include "irprog.h"
#include "irverify.h"
#include "lowering.h"
#include "target.h"
#include "timing.h"
#include "tv.h"
#include "typerep.h"
#include "vrp.h"
#endif
......@@ -22,10 +22,25 @@
*/
/**
* Initializes the firm library. Allocates default data structures.
* Initializes the firm library and prepares code generation for the host.
*
* This is a convenince function that does:
*
* ir_init_library();
* ir_target_set_triple(ir_get_host_machine_triple());
* ir_target_init();
*/
FIRM_API void ir_init(void);
/**
* Initializes the firm library but does not prepare for any code generation.
* This is typically followed by target initialization (\see target,
* ir_target_set(), ir_target_set_triple(), ir_target_init()) to get mode_P
* defined, a number of transformations also depend on target information being
* available.
*/
FIRM_API void ir_init_library(void);
/**
* Frees all memory occupied by the firm library.
*/
......
......@@ -87,6 +87,9 @@ typedef struct ir_cdep ir_cdep;
/** @ingroup ir_initializer
* Initializer (for entities) */
typedef union ir_initializer_t ir_initializer_t;
/** @ingroup machine_triple
* Machine triple */
typedef struct ir_machine_triple_t ir_machine_triple_t;
/**
* @ingroup irgwalk
......@@ -314,6 +317,18 @@ typedef enum {
align_non_aligned, /**< The entity is not aligned. */
} ir_align;
/**
* Specifies what happens when a float value is converted to an integer and
* overflow happens.
*/
typedef enum float_int_conversion_overflow_style_t {
ir_overflow_indefinite, /**< the integer indefinite value (=INT_MIN) is
returned. (e.g. x86 does this) */
ir_overflow_min_max, /**< INT_MIN/INT_MAX is returned depending on the
sign of the floatingpoint number. (e.g. sparc
does this). */
} float_int_conversion_overflow_style_t;
typedef struct hook_entry hook_entry_t;
#include "end.h"
......
......@@ -46,18 +46,6 @@ typedef enum ir_mode_arithmetic {
irma_last = irma_x86_extended_float,
} ir_mode_arithmetic;
/**
* Specifies what happens when a float value is converted to an integer and
* overflow happens.
*/
typedef enum float_int_conversion_overflow_style_t {
ir_overflow_indefinite, /**< the integer indefinite value (=INT_MIN) is
returned. (e.g. x86 does this) */
ir_overflow_min_max, /**< INT_MIN/INT_MAX is returned depending on the
sign of the floatingpoint number. (e.g. sparc
does this). */
} float_int_conversion_overflow_style_t;
/**
* Creates a new mode.
*
......
......@@ -105,8 +105,9 @@ FIRM_API void do_gvn_pre(ir_graph *irg);
* @param i First data predecessor involved in if conversion
* @param j Second data predecessor involved in if conversion
*/
typedef int (*arch_allow_ifconv_func)(ir_node *sel, ir_node *mux_false,
ir_node *mux_true);
typedef int (*arch_allow_ifconv_func)(ir_node const *sel,
ir_node const *mux_false,
ir_node const *mux_true);
/**
* Perform If conversion on a graph.
......@@ -510,28 +511,11 @@ FIRM_API int value_not_null(const ir_node *n, const ir_node **confirm);
FIRM_API ir_tarval *computed_value_Cmp_Confirm(ir_node *left, ir_node *right,
ir_relation relation);
/** Type of callbacks for creating entities of the compiler library */
typedef ident *(*compilerlib_name_mangle_t)(ident *id, ir_type *mt);
/**
* Sets the compilerlib entity creation callback that is used to create
* compilerlib function entities.
*
* @param cb the new compilerlib entity creation callback
*/
FIRM_API void set_compilerlib_name_mangle(compilerlib_name_mangle_t cb);
/** Returns the compilerlib entity creation callback. */
FIRM_API compilerlib_name_mangle_t get_compilerlib_name_mangle(void);
/**
* Constructs the entity for a given function using the current compilerlib
* entity creation callback.
*
* @param id the identifier of the compilerlib function
* @param mt the method type of the compilerlib function
*/
FIRM_API ir_entity *create_compilerlib_entity(ident *id, ir_type *mt);
FIRM_API ir_entity *create_compilerlib_entity(char const *name, ir_type *mt);
/** @} */
......
/*
* This file is part of libFirm.
* Copyright (C) 2017 Matthias Braun <matze@braunis.de>
*/
#ifndef FIRM_TARGET_H
#define FIRM_TARGET_H
#include "firm_types.h"
#include "begin.h"
/**
* @defgroup target Target Setup and Information
* @{
*/
/**
* Prepares code generation for the target specifiy by @p target_triple.
* \see machine_triple describes the format of @p target_triple.
* Note that preparation isn't finished until ir_target_init() is called.
*
* @returns 1 if successfull, 0 if @p target_triple is malformed or unsupported.
*/
FIRM_API int ir_target_set(const char *target_triple);
/**
* Prepares code generation for the target specified by @p machine.
* Note that preparation isn't finished until ir_target_init() is called.
*
* @returns 1 if successfull, 0 if @p machine is malformed or unsupported.
*/
FIRM_API int ir_target_set_triple(ir_machine_triple_t const *machine);
/**
* Configures the current target.
*
* Passes an option to furhter configure the currently selected target. This is
* allowed after calling ir_target_set() or ir_target_set_triple() but not after
* calling ir_target_init().
*
* Some available options:
*
* - omitfp[=0/1] Try to produce code without using a frame pointer.
* - pic[=0/1] Produce position independent code.
* - noplt[=0/1] Avoid using a PLT in position independent code.
* - verboseasm[=0/1] Annotate assembler with verbose comments
* - help Print a list of available options.
*
* The exact set of options is target and platform specific.
*
* @returns -1 if 'help' was found, 0 if the argument could not be parsed,
* 1 if the option could be set.
*/
FIRM_API int ir_target_option(char const *option);
/**
* Initializes the code generation target.
* This must be called after using ir_target_set() or ir_target_set_triple()
* and possibly ir_target_option(). This defines mode_P and finalizes target and
* platform information which may be queried after.
*/
FIRM_API void ir_target_init(void);
/** Returns a message if the current target is considered experimental. */
FIRM_API char const *ir_target_experimental(void);
/** Returns 1 if target uses big endian byte order, 0 if little endian. */
FIRM_API int ir_target_big_endian(void);
/** Returns the biggest alignment required for any target data access. */
FIRM_API unsigned ir_target_biggest_alignment(void);
/** Returns the size of a pointer in bytes for the target. */
FIRM_API unsigned ir_target_pointer_size(void);
/** Returns 1 if target supports position independent code, 0 otherwise. */
FIRM_API int ir_target_supports_pic(void);
/**
* Returns 1 if unaligned memory accesses are (nearly) as fast as aligned
* ones, 0 otherwise.
*/
FIRM_API int ir_target_fast_unaligned_memaccess(void);
/**
* Returns supported float arithmetic mode or NULL if mode_D and mode_F
* are supported natively.
* Some backends like x87 can only do arithmetic in a specific float
* mode (load/store are still done in the "normal" float/double modes).
*/
FIRM_API ir_mode *ir_target_float_arithmetic_mode(void);
/**
* @}
*/
/**
* @ingroup target
* @defgroup platform Target platform information
*
* The functions in this API return information about the operating system or
* runtime system. This contains information about the platform ABI such as the
* size and alignment of C types, the name mangling of global symbols and a list
* of platform and target specific C preprocessor definitions.
* @{
*/
/**
* Object types from the C programming language.
* This is a simplified list that does not differentiate between signed and
* unsigned types.
*/
typedef enum ir_platform_type_t {
IR_TYPE_BOOL,
IR_TYPE_CHAR,
IR_TYPE_SHORT,
IR_TYPE_INT,
IR_TYPE_LONG,
IR_TYPE_LONG_LONG,
IR_TYPE_FLOAT,
IR_TYPE_DOUBLE,
IR_TYPE_LONG_DOUBLE
} ir_platform_type_t;
typedef struct ir_platform_define_t ir_platform_define_t;
/**
* May return an override to long long and double alignment in structs.
*
* This is a special case: Old C compilers had a bug where long long and double
* where aligned differently inside structs. Some ABIs explicitely stay
* compatible to this.
*/
FIRM_API unsigned ir_platform_long_long_and_double_struct_align_override(void);
/** Returns 1 if target should compile position independent code by default. */
FIRM_API int ir_platform_pic_is_default(void);
/**
* Returns 1 if target and platform support thread local storage, 0 otherwise.
*/
FIRM_API int ir_platform_supports_thread_local_storage(void);
/**
* Returns the value that the proprocessor define @p define should be defined
* to.
*/
FIRM_API char const *ir_platform_define_value(
ir_platform_define_t const *define);
/**
* Returns the C type corresponding to wchar_t.
* Note that technically in C++ wchar_t is an own type; this function will
* return a type with the same characteristics anyway.
*/
FIRM_API ir_platform_type_t ir_platform_wchar_type(void);
/** Returns true if the wchar_t type is signed. */
FIRM_API int ir_platform_wchar_is_signed(void);
/** Returns C type used for pointer sized integer like intptr_t or size_t. */
FIRM_API ir_platform_type_t ir_platform_intptr_type(void);
/** Returns the size of the C type @p type in bytes. */
FIRM_API unsigned ir_platform_type_size(ir_platform_type_t type);
/** Returns the alignment of the C type @p type in bytes. */
FIRM_API unsigned ir_platform_type_align(ir_platform_type_t type);
/** Returns an ir_mode matching for the type @p type. */
FIRM_API ir_mode *ir_platform_type_mode(ir_platform_type_t type, int is_signed);
/**
* Returns type used for va_list values. May be a pointer type, a struct type
* or NULL if the backend does not support variadic functions.
*/
FIRM_API ir_type *ir_platform_va_list_type(void);
/** Returns the character "normal" linker identifiers are prefixed with. */
FIRM_API char ir_platform_user_label_prefix(void);
/** Returns the default executable name for the target (a.out or a.exe). */
FIRM_API char const *ir_platform_default_exe_name(void);
/**
* Returns name mangled for platform. This usually adds the user_label_prefix
* ('_' on win32, macOS, "" on ELF based systems) in front of the identifier.
*/
FIRM_API ident *ir_platform_mangle_global(char const *name);
/** Returns the first element in the list of target preprocessor defines. */
FIRM_API ir_platform_define_t const *ir_platform_define_first(void);
/**
* Returns the element following @p define in the list of target preprocessor
* defines or NULL if @p define is the last element.
*/
FIRM_API ir_platform_define_t const *ir_platform_define_next(
ir_platform_define_t const *define);
/** Returns the name of the preprocessor define @p define. */
FIRM_API char const *ir_platform_define_name(
ir_platform_define_t const *define);
/**
* @}
*/
/**
* @ingroup target
* @defgroup machine_triple Machine Triple
*
* Functions to work with machine triples.
*
* A machine triple describes a combination of CPU type, manufacturer and
* operating system. A machine triple is specified as a single string of the
* form 'cpu-manufacturer-system' where 'system' may be the name of the
* operating system or just the name of the kernel. This is the same concept as
* the target triplet in autoconfs config.guess/config.sub. Or LLVMs triple.
*
* The manufacturer and system part may be left out, "none" or "unknown".
* There is some built-in disambiguation logic that recognizes linux-gnu as the
* operating system even when the manufacturer was left out.
*
* "unknown" and "elf" may be used for the operating system resulting in
* ELF objects being produced without assumptions about the operating system.
*
* Examples:
*
* Triple | CPU | Manufacturer | Operating System
* ----------------------- | ------ | ------------- | -----------------
* i686-linux-gnu | i686 | unknown | linux-gnu
* i686-unknown-linux-gnu | i686 | unknown | linux-gnu
* x86_64-pc-mingw32 | x86_64 | pc | mingw32
* i686-apple-darwin | x86_64 | apple | darwin
* x86_64-apple-darwin16.4 | x86_64 | apple | darwin16.4
* sparc-leon-elf | sparc | leon | elf
* arm-eabi | arm | unknown | eabi
* arm-none-eabi | arm | unknown | eabi
* mips-- | mips | unknown | unknown
* mips- | mips | unknown | unknown
* mips | mips | unknown | unknown
*
* Contrary to most functions, the triple manipulation functions may be used
* safely before ir_init() is called.
* @{
*/
/**
* Parses machine triple string @p triple_string into a ir_machine_triple_t
* structure.
*/
FIRM_API ir_machine_triple_t *ir_parse_machine_triple(
const char *triple_string);
/**
* Creates a machine triple matching the host machine. Always succeeds
* or calls panic().
*/
FIRM_API ir_machine_triple_t *ir_get_host_machine_triple(void);
/** Returns the CPU type part of machine triple @p triple. */
FIRM_API const char *ir_triple_get_cpu_type(const ir_machine_triple_t *triple);
/** Returns the manufacturer part of machine triple @p triple. */
FIRM_API const char *ir_triple_get_manufacturer(
const ir_machine_triple_t *triple);
/** Returns the operating system part of machine triple @p triple. */
FIRM_API const char *ir_triple_get_operating_system(
const ir_machine_triple_t *triple);
/** Changes CPU type of @p triple to @p cpu_type. */
FIRM_API void ir_triple_set_cpu_type(ir_machine_triple_t *triple,
const char *cpu_type);
/** Frees the machine triple structure @p triple. */
FIRM_API void ir_free_machine_triple(ir_machine_triple_t *triple);
/**
* @}
*/
#include "end.h"
#endif
......@@ -21,9 +21,11 @@
#include "gen_TEMPLATE_regalloc_if.h"
#include "iredges_t.h"
#include "irprog_t.h"
#include "isas.h"
#include "lower_builtins.h"
#include "lower_calls.h"
#include "panic.h"
#include "target_t.h"
/**
* Transforms the standard firm graph into a TEMPLATE firm graph
......@@ -106,10 +108,12 @@ static void TEMPLATE_generate_code(FILE *output, const char *cup_name)
static void TEMPLATE_init(void)
{
ir_mode *const ptr_mode = new_reference_mode("p32", 32, 32);
set_modeP(ptr_mode);
TEMPLATE_register_init();
TEMPLATE_create_opcodes();
ir_target.experimental
= "The TEMPLATE backend is just a demo for writing backends";
ir_target.float_int_overflow = ir_overflow_min_max;
}
static void TEMPLATE_finish(void)
......@@ -127,35 +131,6 @@ static void TEMPLATE_lower_for_target(void)
be_after_irp_transform("lower-calls");
}
static int TEMPLATE_is_mux_allowed(ir_node *sel, ir_node *mux_false,
ir_node *mux_true)
{
(void)sel;
(void)mux_false;
(void)mux_true;
return false;
}
/**
* Returns the libFirm configuration parameter for this backend.
*/
static const backend_params *TEMPLATE_get_backend_params(void)
{
static backend_params p = {
.experimental = "The TEMPLATE backend is just a demo for writing backends",
.byte_order_big_endian = false,
.pic_supported = false,
.unaligned_memaccess_supported = false,
.modulo_shift = 32,
.allow_ifconv = TEMPLATE_is_mux_allowed,
.machine_size = 32,
.mode_float_arithmetic = NULL,
.type_long_double = NULL,
.float_int_overflow = ir_overflow_min_max,
};
return &p;
}
static unsigned TEMPLATE_get_op_estimated_cost(const ir_node *node)
{
if (is_TEMPLATE_Load(node))
......@@ -165,14 +140,19 @@ static unsigned TEMPLATE_get_op_estimated_cost(const ir_node *node)
return 1;
}
static arch_isa_if_t const TEMPLATE_isa_if = {
arch_isa_if_t const TEMPLATE_isa_if = {
.name = "TEMPLATE",
.pointer_size = 4,
.modulo_shift = 32,
.big_endian = false,
.po2_biggest_alignment = 3,
.pic_supported = false,
.n_registers = N_TEMPLATE_REGISTERS,
.registers = TEMPLATE_registers,
.n_register_classes = N_TEMPLATE_CLASSES,
.register_classes = TEMPLATE_reg_classes,
.init = TEMPLATE_init,
.finish = TEMPLATE_finish,
.get_params = TEMPLATE_get_backend_params,
.generate_code = TEMPLATE_generate_code,
.lower_for_target = TEMPLATE_lower_for_target,
.is_valid_clobber = be_default_is_valid_clobber,
......@@ -182,6 +162,5 @@ static arch_isa_if_t const TEMPLATE_isa_if = {
BE_REGISTER_MODULE_CONSTRUCTOR(be_init_arch_TEMPLATE)
void be_init_arch_TEMPLATE(void)
{
be_register_isa_if("TEMPLATE", &TEMPLATE_isa_if);
TEMPLATE_init_transform();
}
......@@ -33,12 +33,15 @@
#include "irgwalk.h"
#include "iropt_t.h"
#include "irtools.h"
#include "isas.h"
#include "lower_alloc.h"
#include "lower_builtins.h"
#include "lower_calls.h"
#include "lower_mode_b.h"
#include "lowering.h"
#include "panic.h"
#include "platform_t.h"