Cthulhu  0.2.10
Cthulhu compiler collection
ssa.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 
3 #include "base/panic.h"
4 #include "llvm-target/target.h"
5 
6 #include "cthulhu/ssa/ssa.h"
7 
8 #include "core/macros.h"
9 #include "notify/notify.h"
10 #include "std/map.h"
11 #include "std/str.h"
12 #include "std/typed/vector.h"
13 #include "std/vector.h"
14 
15 #include <llvm-c/Core.h>
16 #include <llvm-c/Target.h>
17 #include <llvm-c/TargetMachine.h>
18 #include <llvm-c/Analysis.h>
19 
20 typedef struct llvm_target_t
21 {
23 
24  map_t *types; // map_t<ssa_type_t*, LLVMTypeRef>
26 
27 static LLVMContextRef gContext = NULL;
28 static char *gTargetTriple = NULL;
29 static LLVMTargetRef gTarget = NULL;
30 static LLVMTargetMachineRef gMachine = NULL;
31 static LLVMTargetDataRef gDataLayout = NULL;
32 
33 // ^(?!(typedef|\*)).*LLVM.*([\S\s]|[,])\‍)
34 
35 static const diagnostic_t *get_diagnostic_level(LLVMDiagnosticSeverity severity)
36 {
37  switch (severity)
38  {
39  case LLVMDSError: return &kEvent_LLVMFatalEvent;
40  case LLVMDSWarning: return &kEvent_LLVMWarnEvent;
41  case LLVMDSRemark: return &kEvent_LLVMRemarkEvent;
42  case LLVMDSNote: return &kEvent_LLVMNoteEvent;
43  }
44 
45  return &kEvent_LLVMFatalEvent;
46 }
47 
48 static void llvm_report_callback(LLVMDiagnosticInfoRef info, void *ctx)
49 {
50  target_runtime_t *self = ctx;
51 
52  char *message = LLVMGetDiagInfoDescription(info);
53  const diagnostic_t *event = get_diagnostic_level(LLVMGetDiagInfoSeverity(info));
54  const node_t *node = broker_get_node(self->broker);
55 
56  msg_notify(self->logger, event, node, "%s", message);
57 
58  LLVMDisposeMessage(message);
59 }
60 
62 {
63  LLVMInitializeAllTargetInfos();
64  LLVMInitializeAllTargets();
65  LLVMInitializeAllTargetMCs();
66  LLVMInitializeAllAsmPrinters();
67  LLVMInitializeAllAsmParsers();
68 
69  gContext = LLVMContextCreate();
70  if (!gContext)
71  CT_PANIC("Failed to create LLVM context");
72 
73  LLVMContextSetDiagnosticHandler(gContext, llvm_report_callback, runtime);
74 
75  gTargetTriple = LLVMGetDefaultTargetTriple();
76 
77  char *error = NULL;
78  if (LLVMGetTargetFromTriple(gTargetTriple, &gTarget, &error))
79  CT_PANIC("Failed to get target %s: %s", gTargetTriple, error);
80 
81  gMachine = LLVMCreateTargetMachine(gTarget, gTargetTriple, "generic", "", LLVMCodeGenLevelDefault, LLVMRelocStatic, LLVMCodeModelDefault);
82 
83  gDataLayout = LLVMCreateTargetDataLayout(gMachine);
84 }
85 
87 {
88  // empty
89  CT_UNUSED(runtime);
90 
91  LLVMDisposeTargetData(gDataLayout);
92  LLVMDisposeTargetMachine(gMachine);
93  LLVMDisposeMessage(gTargetTriple);
94  LLVMContextDispose(gContext);
95 
96  LLVMShutdown();
97 }
98 
99 static LLVMTypeRef get_llvm_type(llvm_target_t *self, const ssa_type_t *type);
100 
101 // only uses the width as llvm digit types sign depends on instructions used
102 // rather than the type itself
103 static LLVMTypeRef get_llvm_digit_type(LLVMContextRef ctx, ssa_type_digit_t digit)
104 {
105  // TODO: this may depend on target abi
106  // currently copies what stdint.h does on windows
107  digit_t width = digit.digit;
108  switch (width)
109  {
110  case eDigit8: case eDigitChar:
111  case eDigitLeast8: case eDigitFast8:
112  return LLVMInt8TypeInContext(ctx);
113  case eDigit16: case eDigitShort:
114  case eDigitLeast16:
115  return LLVMInt16TypeInContext(ctx);
116  case eDigit32: case eDigitInt:
117  case eDigitLong: case eDigitLeast32:
118  case eDigitFast16: case eDigitFast32:
119  return LLVMInt32TypeInContext(ctx);
120  case eDigit64: case eDigitLongLong:
121  case eDigitLeast64: case eDigitFast64:
122  case eDigitMax: case eDigitSize: case eDigitPtr: // TODO: this assumes 64 bit always
123  return LLVMInt64TypeInContext(ctx);
124 
125  // TODO: is this how we want to handle floats?
126  case eDigitHalf:
127  return LLVMHalfTypeInContext(ctx);
128  case eDigitFloat:
129  return LLVMFloatTypeInContext(ctx);
130  case eDigitDouble:
131  return LLVMDoubleTypeInContext(ctx);
132 
133  default:
134  CT_NEVER("Invalid digit width %s", digit_name(width));
135  }
136 }
137 
138 static LLVMTypeRef get_llvm_pointer_type(llvm_target_t *self, ssa_type_pointer_t ptr)
139 {
140  LLVMTypeRef inner = get_llvm_type(self, ptr.pointer);
141  if (ptr.length == 0)
142  {
143  return LLVMPointerType(inner, 0);
144  }
145 
146  return LLVMArrayType2(inner, ptr.length);
147 }
148 
149 static LLVMTypeRef get_llvm_closure_type(llvm_target_t *self, ssa_type_closure_t closure)
150 {
151  size_t len = typevec_len(closure.params);
152  LLVMTypeRef *params = ARENA_MALLOC(len * sizeof(LLVMTypeRef), "llvm_closure_params", NULL, self->arena);
153  for (size_t i = 0; i < len; i++)
154  {
155  const ssa_type_t *param = typevec_offset(closure.params, i);
156  params[i] = get_llvm_type(self, param);
157  }
158 
159  LLVMTypeRef ret = get_llvm_type(self, closure.result);
160 
161  return LLVMFunctionType(ret, params, len, closure.variadic);
162 }
163 
164 static LLVMTypeRef get_llvm_enum_type(llvm_target_t *self, ssa_type_enum_t it)
165 {
166  return get_llvm_type(self, it.underlying);
167 }
168 
169 static LLVMTypeRef get_llvm_struct_type(llvm_target_t *self, ssa_type_record_t it)
170 {
171  size_t len = typevec_len(it.fields);
172  LLVMTypeRef *fields = ARENA_MALLOC(len * sizeof(LLVMTypeRef), "llvm_struct_fields", NULL, self->arena);
173  for (size_t i = 0; i < len; i++)
174  {
175  const ssa_field_t *field = typevec_offset(it.fields, i);
176  fields[i] = get_llvm_type(self, field->type);
177  }
178 
179  // TODO: support packed structs
180  return LLVMStructTypeInContext(gContext, fields, len, false);
181 }
182 
183 static LLVMTypeRef get_llvm_union_type(llvm_target_t *self, ssa_type_record_t it)
184 {
185  // get the largest field size
186  size_t len = typevec_len(it.fields);
187  size_t largest = 0;
188  for (size_t i = 0; i < len; i++)
189  {
190  const ssa_field_t *field = typevec_offset(it.fields, i);
191  LLVMTypeRef type = get_llvm_type(self, field->type);
192  size_t size = LLVMStoreSizeOfType(gDataLayout, type);
193  if (size > largest)
194  largest = size;
195  }
196 
197  return LLVMArrayType2(LLVMInt8TypeInContext(gContext), largest);
198 }
199 
200 static LLVMTypeRef get_llvm_type_inner(llvm_target_t *self, const ssa_type_t *type)
201 {
202  switch (type->kind)
203  {
204  case eTypeUnit:
205  case eTypeEmpty: // TODO: empty is not the same as void/unit
206  return LLVMVoidTypeInContext(gContext);
207  case eTypeBool:
208  return LLVMInt1TypeInContext(gContext);
209  case eTypeDigit:
210  return get_llvm_digit_type(gContext, type->digit);
211  case eTypeClosure:
212  return get_llvm_closure_type(self, type->closure);
213  case eTypePointer:
214  return LLVMPointerType(get_llvm_pointer_type(self, type->pointer), 0);
215  case eTypeEnum:
216  return get_llvm_enum_type(self, type->sum);
217  case eTypeOpaque:
218  return LLVMPointerTypeInContext(gContext, 0);
219  case eTypeStruct:
220  return get_llvm_struct_type(self, type->record);
221  case eTypeUnion:
222  return get_llvm_union_type(self, type->record);
223 
224  default:
225  CT_NEVER("Invalid type kind %s", ssa_type_name(type->kind));
226  }
227 }
228 
229 static LLVMTypeRef get_llvm_type(llvm_target_t *self, const ssa_type_t *type)
230 {
231  LLVMTypeRef ref = map_get(self->types, type);
232  if (ref != NULL)
233  return ref;
234 
235  ref = get_llvm_type_inner(self, type);
236  map_set(self->types, type, ref);
237 
238  return ref;
239 }
240 
241 static LLVMValueRef get_digit_value(LLVMTypeRef type, const mpz_t value, ssa_type_digit_t digit)
242 {
243  return LLVMConstIntOfString(type, mpz_get_str(NULL, 10, value), digit.sign == eSignSigned);
244 }
245 
246 static LLVMValueRef get_llvm_value(LLVMTypeRef type, const ssa_value_t *value)
247 {
248  if (!value->init)
249  return LLVMGetUndef(type);
250 
251  switch (value->type->kind)
252  {
253  case eTypeDigit:
254  return get_digit_value(type, value->digit_value, value->type->digit);
255  case eTypeBool:
256  return LLVMConstInt(type, value->bool_value, false);
257  case eTypeOpaque: case eTypePointer: // TODO: need to reconstruct strings from pointer values...
258  return LLVMConstPointerNull(type);
259 
260  default:
261  CT_NEVER("Invalid value type %s", ssa_type_name(value->type->kind));
262  }
263 }
264 
265 static void llvm_build_module(llvm_target_t *self, const ssa_module_t *mod)
266 {
267  LLVMModuleRef modref = LLVMModuleCreateWithNameInContext(mod->name, gContext);
268  LLVMSetModuleDataLayout(modref, gDataLayout);
269  LLVMSetTarget(modref, gTargetTriple);
270 
271  size_t vars = vector_len(mod->globals);
272  for (size_t i = 0; i < vars; i++)
273  {
274  const ssa_symbol_t *global = vector_get(mod->globals, i);
275  ssa_storage_t storage = global->storage;
276  LLVMTypeRef type = get_llvm_type(self, global->type);
277 
278  if (storage.size > 1)
279  type = LLVMArrayType2(type, storage.size);
280 
281  LLVMValueRef ref = LLVMAddGlobal(modref, type, global->name);
282  LLVMSetGlobalConstant(ref, storage.quals & eQualConst);
283 
284  if (global->value != NULL)
285  {
286  LLVMValueRef init = get_llvm_value(type, global->value);
287  LLVMSetInitializer(ref, init);
288  }
289  }
290 
291  char *message;
292  if (LLVMVerifyModule(modref, LLVMReturnStatusAction, &message))
293  CT_NEVER("Failed to verify module %s: %s", mod->name, message);
294 
295  LLVMDisposeMessage(message);
296 }
297 
299 {
300  vector_t *mods = ssa->modules;
301 
302  CT_UNUSED(runtime);
303  CT_UNUSED(mods);
304  CT_UNUSED(emit);
305 
306  llvm_target_t info = {
307  .arena = runtime->arena,
308  .types = map_new(64, kTypeInfoPtr, runtime->arena)
309  };
310 
311  size_t len = vector_len(mods);
312  for (size_t i = 0; i < len; i++)
313  {
314  const ssa_module_t *mod = vector_get(mods, i);
315  llvm_build_module(&info, mod);
316  }
317 
318  emit_result_t result = {
319  .files = vector_new(0, runtime->arena)
320  };
321 
322  return result;
323 }
CT_NODISCARD size_t size
Definition: scan.h:128
STA_DECL const char * ssa_type_name(ssa_kind_t kind)
Definition: ssa.c:1043
CT_BROKER_API const node_t * broker_get_node(broker_t *broker)
Definition: broker.c:333
CT_STD_API void map_set(map_t *map, const void *key, void *value)
set a key-value pair in a map
Definition: map.c:294
CT_NODISCARD CT_STD_API map_t * map_new(size_t size, hash_info_t info, arena_t *arena)
create a new map on the heap
Definition: map.c:113
CT_NODISCARD CT_PUREFN CT_STD_API void * map_get(const map_t *map, const void *key)
get a value from a map
Definition: map.c:324
#define CT_UNUSED(x)
mark a variable as unused
Definition: macros.h:46
#define ARENA_MALLOC(size, name, parent, arena)
allocate memory from a custom allocator
Definition: arena.h:392
CT_NOTIFY_API event_builder_t msg_notify(INOUT_NOTNULL logger_t *logs, const diagnostic_t *diagnostic, const node_t *node, STA_FORMAT_STRING const char *fmt,...)
notify the logger of a new message
#define CT_NEVER(...)
assert that a code path is never reached
Definition: panic.h:136
#define CT_PANIC(...)
panic with a message and optional format arguments
Definition: panic.h:72
CT_TREE_API const char * digit_name(digit_t digit)
get the pretty name of a digit
Definition: ops.c:102
digit_t
digit width
Definition: ops.h:96
CT_STD_API const hash_info_t kTypeInfoPtr
type information for a generic pointer this operates on the pointer itself and not the data it points...
Definition: typeinfo.c:41
CT_NODISCARD CT_PUREFN CT_STD_API size_t typevec_len(const typevec_t *vec)
get the length of a vector
Definition: vector.c:120
CT_NODISCARD CT_PUREFN CT_STD_API void * typevec_offset(const typevec_t *vec, size_t index)
get a pointer to the value at the given index
Definition: vector.c:191
CT_NODISCARD CT_PUREFN CT_STD_API void * vector_get(const vector_t *vector, size_t index)
get a value from a vector
Definition: vector.c:134
CT_NODISCARD CT_STD_API vector_t * vector_new(size_t size, arena_t *arena)
create a new vector with an initial capacity
Definition: vector.c:63
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
Definition: vector.c:152
an allocator object
Definition: arena.h:86
a diagnostic
Definition: diagnostic.h:27
vector_t * files
Definition: broker.h:224
map_t * types
Definition: ssa.c:24
arena_t * arena
Definition: ssa.c:22
an unordered hash map
Definition: map.h:38
a position in a source file
Definition: node.h:23
const ssa_type_t * type
Definition: ssa.h:108
vector_t * globals
vector<ssa_symbol_t> all globals declared/imported/exported by this module
Definition: ssa.h:358
const char * name
Definition: ssa.h:355
vector_t * modules
Definition: ssa.h:363
ssa underlying storage type
Definition: ssa.h:78
size_t size
the number of elements in the storage
Definition: ssa.h:83
tree_quals_t quals
the qualifiers of the storage
Definition: ssa.h:86
const ssa_type_t * type
the public facing type of this symbol
Definition: ssa.h:341
ssa_storage_t storage
the backing storage for this symbol
Definition: ssa.h:342
const ssa_value_t * value
the value of this symbol, must always be set for globals
Definition: ssa.h:344
const char * name
internal name
Definition: ssa.h:340
const ssa_type_t * result
Definition: ssa.h:131
bool variadic
Definition: ssa.h:133
typevec_t * params
Definition: ssa.h:132
digit_t digit
Definition: ssa.h:117
sign_t sign
Definition: ssa.h:116
ssa_type_t * underlying
Definition: ssa.h:126
const ssa_type_t * pointer
Definition: ssa.h:137
size_t length
Definition: ssa.h:138
typevec_t * fields
Definition: ssa.h:142
ssa_kind_t kind
Definition: ssa.h:146
ssa_type_record_t record
Definition: ssa.h:154
ssa_type_closure_t closure
Definition: ssa.h:152
ssa_type_digit_t digit
Definition: ssa.h:151
ssa_type_enum_t sum
Definition: ssa.h:155
ssa_type_pointer_t pointer
Definition: ssa.h:153
bool init
whether this value has been initialized
Definition: ssa.h:180
const ssa_type_t * type
Definition: ssa.h:178
arena_t * arena
Definition: broker.h:302
a generic vector of pointers
Definition: vector.c:16
void llvm_destroy(target_runtime_t *runtime)
Definition: ssa.c:86
emit_result_t llvm_ssa(target_runtime_t *runtime, const ssa_result_t *ssa, target_emit_t *emit)
Definition: ssa.c:298
void llvm_create(target_runtime_t *runtime)
Definition: ssa.c:61