summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorManuel Mohr <manuel.mohr@kit.edu>2017-01-02 11:48:51 +0100
committerPhilipp Serrer <philipp@serrer.de>2017-12-08 19:09:05 +0100
commitc1cb1ae4b066fbafd7a55d257f3c4cc6a736f738 (patch)
treed3e3477700f9d74cc0c47a7bdcc46e81bb25eb03
parent9dc4dd2e3b90d63d9af55380b840ee7a0cd82b97 (diff)
Support loading (and linking) multiple IR files.lto
-rw-r--r--ir/ir/irio.c94
-rw-r--r--ir/ir/irlink.c131
-rw-r--r--ir/ir/irlink_t.h15
3 files changed, 228 insertions, 12 deletions
diff --git a/ir/ir/irio.c b/ir/ir/irio.c
index afc6ed0..28cc347 100644
--- a/ir/ir/irio.c
+++ b/ir/ir/irio.c
@@ -16,6 +16,8 @@
#include "irgmod.h"
#include "irgraph_t.h"
#include "irgwalk.h"
+#include "irio_t.h"
+#include "irlink_t.h"
#include "irprintf.h"
#include "irprog_t.h"
#include "obst.h"
@@ -1598,6 +1600,17 @@ static ir_initializer_t *read_initializer(read_env_t *env)
panic("unknown initializer kind");
}
+static ir_type *get_segment_by_name(ident *id)
+{
+ const char *name = get_id_str(id);
+ for (unsigned s = IR_SEGMENT_FIRST; s <= IR_SEGMENT_LAST; ++s) {
+ ir_type *type = get_segment_type(s);
+ if (streq(name, type->name))
+ return type;
+ }
+ return NULL;
+}
+
/** Reads a type description and remembers it by its id. */
static void read_type(read_env_t *env)
{
@@ -1683,7 +1696,9 @@ static void read_type(read_env_t *env)
case tpo_segment: {
ident *id = read_ident_null(env);
- type = new_type_segment(id, 0);
+ type = get_segment_by_name(id);
+ if (type == NULL)
+ panic("could not find type for segment \"%s\"", get_id_str(id));
goto finish_type;
}
@@ -1715,6 +1730,26 @@ static void read_unknown_entity(read_env_t *env)
set_id(env, entnr, entity);
}
+static ir_initializer_t* parse_initializer(read_env_t *env)
+{
+ const char *str = read_word(env);
+ if (streq(str, "initializer")) {
+ return read_initializer(env);
+ } else if (streq(str, "none")) {
+ /* do nothing */
+ } else {
+ parse_error(env, "expected 'initializer' or 'none' got '%s'\n", str);
+ }
+ return NULL;
+}
+
+static ident *rename_entity(ident *name)
+{
+ static unsigned rename_count = 0;
+ const char *str = get_id_str(name);
+ return new_id_fmt("r.%d.%s", rename_count++, str);
+}
+
/** Reads an entity description and remembers it by its id. */
static void read_entity(read_env_t *env, ir_entity_kind kind)
{
@@ -1725,8 +1760,9 @@ static void read_entity(read_env_t *env, ir_entity_kind kind)
ir_linkage linkage = IR_LINKAGE_DEFAULT;
ir_type *owner = NULL;
ir_entity *entity = NULL;
+ bool is_named = kind != IR_ENTITY_LABEL && kind != IR_ENTITY_PARAMETER;
- if (kind != IR_ENTITY_LABEL && kind != IR_ENTITY_PARAMETER) {
+ if (is_named) {
name = read_ident(env);
ld_name = read_ident_null(env);
}
@@ -1743,6 +1779,45 @@ static void read_entity(read_env_t *env, ir_entity_kind kind)
ir_volatility volatility = read_volatility(env);
+ // By default everything with a name, which is private is renamed
+ // TODO: this will rename every private entity even if it has been renamed
+ // in a prior import export cylce.
+ bool is_externally_visible = visibility != ir_visibility_local && visibility != ir_visibility_private;
+ if (is_named && !is_externally_visible) {
+ name = rename_entity(name);
+ ld_name = rename_entity(ld_name);
+ }
+
+ // try to merge all entities that have a name and are not private
+ if (is_named && is_externally_visible) {
+ entity = ir_link_entity(name, type, kind, owner, linkage, volatility, visibility);
+
+ if (entity != NULL) {
+ // We might see the initializer for a previously constructed entity here
+ if (kind == IR_ENTITY_NORMAL) {
+ ir_initializer_t *initializer = parse_initializer(env);
+ // only one entity can have an initializer. This entity must be the definition!
+ if (initializer != NULL) {
+ if (get_entity_initializer(entity) == NULL) {
+ // the current entity has no initializer => it is only a declaration
+ // we take over the type and initializer of this entity, because we can not
+ // easily replace the old entity
+ set_entity_type(entity, type);
+ set_entity_initializer(entity, initializer);
+ } else {
+ parse_error(env, "duplicate initializer for entity %s\n", get_id_str(name));
+ }
+ }
+ }
+
+ // Ignore the (possibly) remaining text.
+ skip_to(env, '\n');
+
+ // No new entity is created. Link to the existing entity
+ goto finish_entity;
+ }
+ }
+
switch (kind) {
case IR_ENTITY_ALIAS: {
ir_entity *aliased = read_entity_ref(env);
@@ -1753,16 +1828,9 @@ static void read_entity(read_env_t *env, ir_entity_kind kind)
entity = new_entity(owner, name, type);
if (ld_name != NULL)
set_entity_ld_ident(entity, ld_name);
- const char *str = read_word(env);
- if (streq(str, "initializer")) {
- ir_initializer_t *initializer = read_initializer(env);
- if (initializer != NULL)
- set_entity_initializer(entity, initializer);
- } else if (streq(str, "none")) {
- /* do nothing */
- } else {
- parse_error(env, "expected 'initializer' or 'none' got '%s'\n", str);
- }
+ ir_initializer_t *initializer = parse_initializer(env);
+ if (initializer != NULL)
+ set_entity_initializer(entity, initializer);
break;
case IR_ENTITY_COMPOUND_MEMBER:
entity = new_entity(owner, name, type);
@@ -1808,6 +1876,8 @@ static void read_entity(read_env_t *env, ir_entity_kind kind)
set_entity_visibility(entity, visibility);
set_entity_linkage(entity, linkage);
+finish_entity:
+ ir_adjust_visibility(entity);
set_id(env, entnr, entity);
}
diff --git a/ir/ir/irlink.c b/ir/ir/irlink.c
new file mode 100644
index 0000000..149dc38
--- /dev/null
+++ b/ir/ir/irlink.c
@@ -0,0 +1,131 @@
+/*
+ * This file is part of libFirm.
+ * Copyright (C) 2017 University of Karlsruhe.
+ */
+
+/**
+ * @brief Provide means to link (i.e. merge) entities and types
+ * from multiple IR files.
+ * @author Mark Weinreuter, Manuel Mohr
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include "entity_t.h"
+#include "firm_types.h"
+#include "irflag.h"
+#include "irlink_t.h"
+#include "irmode.h"
+#include "irprog.h"
+#include "util.h"
+
+static ir_visibility get_min_visibility(ir_visibility v1, ir_visibility v2)
+{
+ if (v1 == v2)
+ return v1;
+
+ // if there are different visibilities, choose the non external visibility
+ if (v1 == ir_visibility_external) {
+ return v2;
+ } else if (v2 == ir_visibility_external) {
+ return v1;
+ }
+
+ panic("Incompatible visibilities: %+F vs. %+F\n", v1, v2);
+}
+
+/**
+ * If the function type differs from the already known function type
+ * figure out which is the correct type. This method assumes that implicit
+ * declared functions have zero parameters. Two function types with different
+ * non zero parameter count are regarded as invalid.
+ */
+static ir_type *handle_implicit_declared_functions(ir_type *old_type, ir_type *new_type)
+{
+ size_t new_n = get_method_n_params(new_type);
+ size_t old_n = get_method_n_params(old_type);
+ size_t new_rn = get_method_n_ress(new_type);
+ size_t old_rn = get_method_n_ress(old_type);
+
+ //TODO: what about variadic method types? Can they cause trouble
+ bool new_variadic = is_method_variadic(new_type);
+ bool old_variadic = is_method_variadic(old_type);
+
+ // Since there is no information if a method type is from a definition,
+ // a delceration or an impplicit declaration we try to infer this
+ // information from the amout of parameters and return types
+ // an implicit declaration has no parameters and an integer return value
+ // Compare each parameter type is also not an option since ir_type s
+ // are not merge and cannot be easily compared
+ bool new_maybe_implicit = new_n == 0 && new_rn == 1 && get_type_mode(get_method_res_type(new_type, 0)) == get_modeIs();
+ bool old_maybe_implicit = old_n == 0 && old_rn == 1 && get_type_mode(get_method_res_type(old_type, 0)) == get_modeIs();
+
+ // Implicit method type check
+ if (new_rn != old_rn || new_n != old_n || new_variadic != old_variadic) {
+ // Different amount of parameters or return values.
+ // This is either an implicit declared function or an error
+ // If it is an implicit declaration we go with the other type
+
+ if (old_maybe_implicit) {
+ return new_type;
+ } else if (new_maybe_implicit) {
+ return old_type;
+ }
+
+ // TODO: since there could be a declaration X foo() and X foo(p1, p2, p3)
+ // TODO: we trust the frontend to do its job
+ if (new_n > old_n)
+ return new_type;
+
+ return old_type;
+ }
+
+ // TODO: figure out if one if them is the definition
+ return old_type;
+}
+
+ir_entity *ir_link_entity(ident *name, ir_type *type, ir_entity_kind kind,
+ ir_type *owner, ir_linkage linkage,
+ ir_volatility volatility, ir_visibility visibility)
+{
+ ir_entity *entity = ir_get_global(name);
+
+ // TODO: more comparison checks needed?
+ if (entity == NULL || entity->kind != kind || entity->owner != owner)
+ return NULL;
+
+ visibility = get_min_visibility(get_entity_visibility(entity), visibility);
+ volatility = MAX(get_entity_volatility(entity), volatility); // is_volatile > not_volatile
+ linkage |= get_entity_linkage(entity); // Linkage values accumulate
+
+ if (kind == IR_ENTITY_METHOD) {
+ ir_type *tp = get_entity_type(entity);
+ tp = handle_implicit_declared_functions(tp, type);
+ set_entity_type(entity, tp);
+ }
+
+ set_entity_linkage(entity, linkage);
+ set_entity_volatility(entity, volatility);
+ set_entity_visibility(entity, visibility);
+
+ return entity;
+}
+
+void ir_adjust_visibility(ir_entity *entity)
+{
+ ir_visibility visibility = get_entity_visibility(entity);
+ const char *name = get_id_str(get_entity_ld_name(entity));
+ const bool can_adjust_visibility = get_opt_closed_world();
+ const bool is_externally_visible = visibility != ir_visibility_private && visibility != ir_visibility_local;
+
+ if (can_adjust_visibility && is_externally_visible
+ && entity_has_definition(entity)
+ && name != NULL && !streq(name, "main")) {
+
+ set_entity_visibility(entity, ir_visibility_local);
+ remove_entity_linkage(entity, IR_LINKAGE_MERGE);
+ }
+}
diff --git a/ir/ir/irlink_t.h b/ir/ir/irlink_t.h
new file mode 100644
index 0000000..354b24d
--- /dev/null
+++ b/ir/ir/irlink_t.h
@@ -0,0 +1,15 @@
+/*
+ * This file is part of libFirm.
+ * Copyright (C) 2017 University of Karlsruhe.
+ */
+
+#ifndef LIBFIRM_IR_IR_LINK_H
+#define LIBFIRM_IR_IR_LINK_H
+
+#include "firm_types.h"
+
+ir_entity *ir_link_entity(ident *name, ir_type *type, ir_entity_kind kind, ir_type *owner, ir_linkage linkage, ir_volatility volatility, ir_visibility visibility);
+
+void ir_adjust_visibility(ir_entity *entity);
+
+#endif