Yume
compiler.cpp
Go to the documentation of this file.
1#include "compiler.hpp"
2#include "ast/ast.hpp"
5#include "extra/mangle.hpp"
6#include "qualifier.hpp"
9#include "ty/substitution.hpp"
10#include "ty/type.hpp"
11#include "ty/type_base.hpp"
12#include "util.hpp"
13#include "vals.hpp"
14#include <algorithm>
15#include <exception>
16#include <limits>
17#include <llvm/ADT/STLExtras.h>
18#include <llvm/ADT/StringMap.h>
19#include <llvm/ADT/StringMapEntry.h>
20#include <llvm/ADT/StringRef.h>
21#include <llvm/ADT/Twine.h>
22#include <llvm/ADT/iterator.h>
23#include <llvm/BinaryFormat/Dwarf.h>
24#include <llvm/IR/Argument.h>
25#include <llvm/IR/BasicBlock.h>
26#include <llvm/IR/CallingConv.h>
27#include <llvm/IR/Constant.h>
28#include <llvm/IR/Constants.h>
29#include <llvm/IR/DIBuilder.h>
30#include <llvm/IR/DerivedTypes.h>
31#include <llvm/IR/Function.h>
32#include <llvm/IR/GlobalValue.h>
33#include <llvm/IR/GlobalVariable.h>
34#include <llvm/IR/Instruction.h>
35#include <llvm/IR/Instructions.h>
36#include <llvm/IR/IntrinsicInst.h>
37#include <llvm/IR/LegacyPassManager.h>
38#include <llvm/IR/Type.h>
39#include <llvm/IR/Value.h>
40#include <llvm/IR/Verifier.h>
41#include <llvm/Support/Casting.h>
42#include <llvm/Support/CodeGen.h>
43#include <llvm/Support/ErrorHandling.h>
44#include <llvm/Support/Host.h>
45#include <llvm/Support/TargetSelect.h>
46#include <llvm/Support/raw_ostream.h>
47#include <llvm/Target/TargetOptions.h>
48// TODO(LLVM MIN >= 14): remove workaround
49#if __has_include(<llvm/MC/TargetRegistry.h>)
50#include <llvm/MC/TargetRegistry.h>
51#else
52#include <llvm/Support/TargetRegistry.h>
53#endif
54#include <optional>
55#include <sstream>
56#include <stdexcept>
57#include <type_traits>
58#include <utility>
59#include <variant>
60
61namespace yume {
62static auto make_cdtor_fn(llvm::IRBuilder<>& builder, llvm::Module& module, bool is_ctor) -> llvm::Function* {
63 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
64 static auto* global_cdtor_fn_ty = llvm::FunctionType::get(builder.getVoidTy(), false);
65 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
66 static auto* global_cdtor_entry_ty =
67 llvm::StructType::get(builder.getInt32Ty(), global_cdtor_fn_ty->getPointerTo(), builder.getInt8PtrTy());
68 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
69 static auto* global_cdtor_array_ty = llvm::ArrayType::get(global_cdtor_entry_ty, 1);
70
71 auto* global_cdtor_fn = llvm::Function::Create(global_cdtor_fn_ty, llvm::Function::ExternalLinkage,
72 (is_ctor ? "_Ym.__ctor" : "_Ym.__dtor"), &module);
73 auto* bb = llvm::BasicBlock::Create(module.getContext(), "entry", global_cdtor_fn);
74 builder.SetInsertPoint(bb);
75 builder.CreateRetVoid();
76
77 (void)new llvm::GlobalVariable(
78 module, global_cdtor_array_ty, true, llvm::GlobalVariable::AppendingLinkage,
79 llvm::ConstantArray::get(
80 global_cdtor_array_ty,
81 llvm::ConstantStruct::get(global_cdtor_entry_ty, builder.getInt32(std::numeric_limits<uint16_t>::max()),
82 global_cdtor_fn, llvm::ConstantPointerNull::get(builder.getInt8PtrTy()))),
83 (is_ctor ? "llvm.global_ctors" : "llvm.global_dtors"));
84
85 return global_cdtor_fn;
86}
87
88Compiler::Compiler(const optional<string>& target_triple, vector<SourceFile> source_files)
89 : m_sources(move(source_files)), m_walker(std::make_unique<semantic::TypeWalker>(*this)) {
90 m_context = std::make_unique<llvm::LLVMContext>();
91 m_module = std::make_unique<llvm::Module>("yume", *m_context);
92 m_debug = std::make_unique<llvm::DIBuilder>(*m_module);
93 m_module->addModuleFlag(llvm::Module::Warning, "Debug Info Version", llvm::DEBUG_METADATA_VERSION);
94
95 m_builder = std::make_unique<llvm::IRBuilder<>>(*m_context);
96
97 llvm::InitializeNativeTarget();
98 llvm::InitializeNativeTargetAsmParser();
99 llvm::InitializeNativeTargetAsmPrinter();
100 string error;
101 const string triple = target_triple.value_or(llvm::sys::getDefaultTargetTriple());
102 const auto* target = llvm::TargetRegistry::lookupTarget(triple, error);
103
104 if (target == nullptr) {
105 errs() << error;
106 throw std::exception();
107 }
108 const char* cpu = "generic";
109 const char* feat = "";
110
111 for (const auto& src_file : m_sources) {
112 auto* debug_file = m_debug->createFile(src_file.path.filename().native(), src_file.path.parent_path().native());
113 auto* compile_unit = m_debug->createCompileUnit(llvm::dwarf::DW_LANG_C, debug_file, "yumec", false, "", 0);
114 m_source_mapping.try_emplace(src_file.program.get(), compile_unit);
115 }
116
117 const llvm::TargetOptions opt;
118 m_target_machine =
119 unique_ptr<llvm::TargetMachine>(target->createTargetMachine(triple, cpu, feat, opt, llvm::Reloc::Model::PIC_));
120
121 m_module->setDataLayout(m_target_machine->createDataLayout());
122 m_module->setTargetTriple(triple);
123
124 m_types.declare_size_type(*this);
125
126 m_global_ctor_fn = make_cdtor_fn(*m_builder, *m_module, true);
127 m_global_dtor_fn = make_cdtor_fn(*m_builder, *m_module, false);
128}
129
130void Compiler::declare_default_ctor(Struct& st) {
131 if (st.ast().is_interface)
132 return; // Don't declare implicit ctors if the struct is an interface
133
134 const bool no_ctors_declared =
135 std::ranges::none_of(m_ctors, [&](const Fn& fn) { return fn.get_self_ty() == st.get_self_ty(); });
136
137 if (!no_ctors_declared)
138 return; // Don't declare implicit ctors if at least one user-defined one exists
139
140 vector<ast::TypeName> ctor_args;
141 vector<ast::AnyStmt> ctor_body;
142 for (auto& field : st.ast().fields) {
143 auto tok = field.token_range();
144 ast::AnyType proxy_type = make_unique<ast::ProxyType>(tok, field.name);
145 ctor_args.emplace_back(field.token_range(), move(proxy_type), field.name);
146
147 auto implicit_field = make_unique<ast::FieldAccessExpr>(tok, std::nullopt, field.name);
148 auto arg_var = make_unique<ast::VarExpr>(tok, field.name);
149 ctor_body.emplace_back(make_unique<ast::AssignExpr>(tok, move(implicit_field), move(arg_var)));
150 }
151 // TODO(rymiel): Give these things sensible locations?
152 auto& new_ct = st.body().body.emplace_back(
153 std::make_unique<ast::CtorDecl>(span<Token>{}, move(ctor_args), ast::Compound({}, move(ctor_body))));
154
155 // if (!st.subs.fully_substituted())
156 // return;
157
158 walk_types(decl_statement(*new_ct, st.get_self_ty(), st.member, &st.get_subs()));
159}
160
161static inline auto vtable_entry_for(const Fn& fn) -> VTableEntry {
162 return {.name = fn.name(), .args = fn.arg_types(), .ret = fn.ret()};
163}
164
165static inline void create_vtable_for(Struct& st) {
166 YUME_ASSERT(st.ast().is_interface, "Cannot create vtable for non-interface struct");
167
168 for (auto& i : st.body()) {
169 if (auto* fn_ast = dyn_cast<ast::FnDecl>(i.raw_ptr())) {
170 if (!fn_ast->abstract())
171 continue;
172 auto* fn = fn_ast->sema_decl;
173 YUME_ASSERT(fn != nullptr, "Semantic Fn not set in FnDecl AST node");
174
175 st.vtable_members.emplace_back(vtable_entry_for(*fn));
176 }
177 }
178}
179
181 m_scope.push_scope(); // Global scope
182
183 for (const auto& source : m_sources)
184 for (auto& i : source.program->body)
185 decl_statement(*i, {}, source.program.get());
186
187 // 1: Only convert the types of constants
188 for (auto& cn : m_consts)
189 walk_types(&cn);
190
191 // 2: Only convert structs
192 for (auto& st : m_structs)
193 walk_types(&st);
194
195 // 3: Convert initializers of constants
196 for (auto& cn : m_consts) {
197 auto* const_ty = llvm_type(cn.ast().ensure_ty());
198 cn.llvm = new llvm::GlobalVariable(*m_module, const_ty, false, llvm::GlobalVariable::PrivateLinkage, nullptr,
199 ".const." + cn.name());
200 }
201
202 // 4: Only convert user defined constructors
203 for (auto& ct : m_ctors)
204 walk_types(&ct);
205
206 // At this point, all _user defined_ constructors have been declared, so we can add implicitly defined constructors to
207 // structs which haven't declared any.
208 for (auto& st : m_structs)
209 declare_default_ctor(st);
210
211 // 5: only convert function parameters
212 for (auto& fn : m_fns)
213 walk_types(&fn);
214
215 // 6: Create vtables for interfaces
216 for (auto& st : m_structs)
217 if (st.ast().is_interface)
219
220 // 7: convert everything else, but only when instantiated
221 m_walker->in_depth = true;
222
223 for (auto& cn : m_consts) {
224 walk_types(&cn);
225 define(cn);
226 }
227
228 // Find all external functions. These will be the "entrypoints".
229 vector<Fn*> extern_fns = {};
230 for (auto& fn : m_fns) {
231 if (fn.name() == "main")
233
234 if (fn.extern_linkage() && !fn.extern_decl())
235 extern_fns.push_back(&fn);
236 }
237
238 if (extern_fns.empty()) {
239 throw std::logic_error("Program is missing any declarations with external linkage. Perhaps you meant to declare a "
240 "`main` function?"); // Related: #10
241 }
242 for (auto* ext : extern_fns)
243 declare(*ext);
244
245 while (!m_decl_queue.empty()) {
246 auto next = m_decl_queue.front();
247 m_decl_queue.pop();
248 next.visit([](std::monostate /*absent*/) { /* nothing to do */ }, //
249 [&](Fn* fn) { define(*fn); }, //
250 [&](Const* cn) { define(*cn); },
251 [&](Struct* /*st*/) { throw std::logic_error("Cannot define a struct"); });
252 }
253
254 YUME_ASSERT(m_scope.size() == 1, "End of compilation should end with only the global scope remaining");
255
256 m_builder->SetInsertPoint(&m_global_dtor_fn->getEntryBlock(), m_global_dtor_fn->getEntryBlock().begin());
257 destruct_last_scope();
258 m_scope.clear();
259
260 m_debug->finalize();
261
262 if (llvm::verifyModule(*m_module, &errs())) {
263 m_module->print(errs(), nullptr, false, true);
264 throw std::runtime_error("Module verification failed");
265 }
266}
267
268void Compiler::walk_types(DeclLike decl_like) {
269 decl_like.visit([](std::monostate /*absent*/) { /* nothing to do */ },
270 [&](auto& decl) {
271 m_walker->current_decl = decl;
272 m_walker->body_statement(decl->ast());
273 m_walker->current_decl = {};
274 });
275}
276
278 auto& s_decl = st.st_ast;
279
280 auto fields = vector<ast::TypeName*>();
281 fields.reserve(s_decl.fields.size());
282 for (auto& f : s_decl.fields)
283 fields.push_back(&f);
284
285 auto iter = m_types.known.find(s_decl.name);
286 if (iter == m_types.known.end()) {
287 auto empl = m_types.known.try_emplace(s_decl.name,
288 std::make_unique<ty::Struct>(s_decl.name, move(fields), &st, &st.get_subs()));
289 YUME_ASSERT((isa<ty::Struct>(*empl.first->second)), "Struct type must be a struct");
290 st.self_ty = &*empl.first->second;
291 return true;
292 }
293
294 YUME_ASSERT((isa<ty::Struct>(*iter->second)), "Struct type must be a struct");
295 auto& existing = cast<ty::Struct>(*iter->second);
296
297 if (!st.get_subs().fully_substituted())
298 return false;
299
300 existing.m_fields = move(fields);
301 st.self_ty = &existing.get_or_create_instantiation(st.get_subs());
302 return true;
303}
304
305auto Compiler::decl_statement(ast::Stmt& stmt, optional<ty::Type> parent, ast::Program* member,
306 nullable<Substitutions*> parent_subs) -> DeclLike {
307 if (auto* fn_decl = dyn_cast<ast::FnDecl>(&stmt)) {
308 vector<GenericKey> generics;
309 vector<unique_ptr<ty::Generic>> primary_generics;
310 for (auto& i : fn_decl->type_args) {
311 if (i.is_type_parameter()) {
312 primary_generics.emplace_back(std::make_unique<ty::Generic>(i.name));
313 generics.emplace_back(i.name);
314 } else {
315 YUME_ASSERT(i.type.has_value(), "Non-type generic parameter must have associated value type");
316 generics.emplace_back(i.name, i.type.raw_ptr());
317 }
318 }
319 auto& fn = m_fns.emplace_back(fn_decl, member, parent, parent_subs, move(generics), move(primary_generics));
320 fn_decl->sema_decl = &fn;
321
322 return &fn;
323 }
324 if (auto* s_decl = dyn_cast<ast::StructDecl>(&stmt)) {
325 // XXX: Lots of duplication from above
326 vector<GenericKey> generics;
327 vector<unique_ptr<ty::Generic>> primary_generics;
328 for (auto& i : s_decl->type_args) {
329 if (i.is_type_parameter()) {
330 primary_generics.emplace_back(std::make_unique<ty::Generic>(i.name));
331 generics.emplace_back(i.name);
332 } else {
333 YUME_ASSERT(i.type.has_value(), "Non-type generic parameter must have associated value type");
334 generics.emplace_back(i.name, i.type.raw_ptr());
335 }
336 }
337 auto& st =
338 m_structs.emplace_back(*s_decl, member, std::nullopt, parent_subs, move(generics), move(primary_generics));
339 if (!create_struct(st)) {
340 m_structs.pop_back();
341 return {};
342 }
343
345 YUME_ASSERT(m_slice_struct == nullptr, "Can't have multiple slice types");
346 m_slice_struct = &st;
347 }
348
349 for (auto& f : s_decl->body) {
350 // if (st.get_subs().empty() || isa<ast::CtorDecl>(*f))
351 decl_statement(*f, st.self_ty, member, &st.get_subs());
352 }
353
354 return &st;
355 }
356 if (auto* ctor_decl = dyn_cast<ast::CtorDecl>(&stmt)) {
357 auto& ctor = m_ctors.emplace_back(ctor_decl, member, parent, parent_subs);
358
359 return &ctor;
360 }
361 if (auto* const_decl = dyn_cast<ast::ConstDecl>(&stmt)) {
362 auto& cn = m_consts.emplace_back(*const_decl, member, parent);
363
364 return &cn;
365 }
366
367 throw std::runtime_error("Invalid top-level statement: "s + stmt.kind_name());
368}
369
370static auto build_function_type(Compiler& compiler, const ty::Function& type) -> llvm::Type* {
371 llvm::StructType* closure_ty = nullptr;
372 llvm::Type* memo = nullptr;
373 auto* erased_closure_ty = compiler.builder()->getInt8PtrTy();
374
375 if (!type.is_fn_ptr()) {
376 auto closured = vector<llvm::Type*>{};
377 for (const auto& i : type.closure())
378 closured.push_back(compiler.llvm_type(i));
379 if (closured.empty()) // Can't have an empty closure type
380 closured.push_back(compiler.builder()->getInt8Ty());
381
382 // Closures are type-erased to just a bag of bits. We store the "real type" of the closure within the function
383 // type for better retrieval, but in practice, they're always passed around bitcasted. Note that the bitcasting
384 // is not required when opaque pointers are in play
385 closure_ty = llvm::StructType::create(closured, "closure");
386 }
387
388 auto args = vector<llvm::Type*>{};
389 if (closure_ty != nullptr)
390 args.push_back(erased_closure_ty); // Lambda takes a closure as the first parameter
391 for (const auto& i : type.args())
392 args.push_back(compiler.llvm_type(i));
393
394 auto* return_type = compiler.builder()->getVoidTy();
395 if (auto ret = type.ret(); ret.has_value())
396 return_type = compiler.llvm_type(*ret);
397
398 auto* fn_ty = llvm::FunctionType::get(return_type, args, type.is_c_varargs());
399
400 if (type.is_fn_ptr())
401 memo = fn_ty->getPointerTo();
402 else
403 memo = llvm::StructType::get(fn_ty->getPointerTo(), erased_closure_ty);
404
405 type.fn_memo(compiler, fn_ty);
406 type.closure_memo(compiler, closure_ty);
407 type.memo(compiler, memo);
408 return memo;
409}
410
411static auto build_struct_type(Compiler& compiler, const ty::Struct& type) -> llvm::Type* {
412 llvm::Type* memo = nullptr;
413
414 // errs() << "build_struct_type for [`" << type.name() << "'@" << &type << "]\n";
415
416 if (type.is_interface()) {
417 memo = llvm::StructType::get(compiler.builder()->getInt8PtrTy(), compiler.builder()->getInt8PtrTy());
418 } else {
419 auto fields = vector<llvm::Type*>{};
420 for (const auto* i : type.fields())
421 fields.push_back(compiler.llvm_type(i->type->ensure_ty()));
422
423 memo = llvm::StructType::create(*compiler.context(), fields, "_"s + type.name());
424 }
425 type.memo(compiler, memo);
426 return memo;
427}
428
429auto Compiler::llvm_type(ty::Type type, bool erase_opaque) -> llvm::Type* {
430 llvm::Type* base = nullptr;
431 bool interface_self = false;
432
433 if (const auto* int_type = type.base_dyn_cast<ty::Int>()) {
434 base = llvm::Type::getIntNTy(*m_context, int_type->size());
435 } else if (const auto* ptr_type = type.base_dyn_cast<ty::Ptr>()) {
436 YUME_ASSERT(ptr_type->qualifier() == Qualifier::Ptr, "Ptr type must hold pointer");
437 base = llvm::PointerType::getUnqual(llvm_type(ptr_type->pointee()));
438 } else if (const auto* struct_type = type.base_dyn_cast<ty::Struct>()) {
439 llvm::Type* memo = struct_type->memo();
440 if (memo == nullptr)
441 memo = build_struct_type(*this, *struct_type);
442
443 base = memo;
444 interface_self = struct_type->is_interface();
445 } else if (const auto* function_type = type.base_dyn_cast<ty::Function>()) {
446 llvm::Type* memo = function_type->memo();
447 if (memo == nullptr)
448 memo = build_function_type(*this, *function_type);
449
450 base = memo;
451 } else if (type.base_isa<ty::Nil>()) {
452 base = llvm::Type::getVoidTy(*m_context);
453 } else if (const auto* opaque_self_type = type.base_dyn_cast<ty::OpaqueSelf>()) {
454 if (erase_opaque)
455 return m_builder->getInt8PtrTy();
456 base = llvm_type({opaque_self_type->indirect()});
457 interface_self = llvm::isa<ty::Struct>(opaque_self_type->indirect()) &&
458 llvm::cast<ty::Struct>(opaque_self_type->indirect())->is_interface();
459 } else if (type.base_isa<ty::Meta>()) {
460 base = m_builder->getInt8Ty();
461 } else {
462 throw std::logic_error("Unknown type base " + type.name());
463 }
464
465 if (type.is_mut() && !interface_self)
466 return base->getPointerTo();
467 if (type.is_opaque_self() && !interface_self)
468 return base->getPointerTo();
469 return base;
470}
471
473 if (type.is_mut()) {
474 const auto deref_type = type.mut_base();
475 if (deref_type.has_value())
476 return destruct(m_builder->CreateLoad(llvm_type(*deref_type), val, "dt.deref"), *deref_type);
477 }
478 if (type.is_slice()) {
479 auto base_type = type.base_cast<ty::Struct>()->fields().at(0)->ensure_ty().ensure_ptr_base(); // ???
480 auto* base_llvm_type = llvm_type(base_type);
481 auto* ptr = m_builder->CreateExtractValue(val, 0, "sl.ptr.free");
482 if (!base_type.is_trivially_destructible()) {
483 auto* size = m_builder->CreateExtractValue(val, 1, "sl.size.free");
484 auto* loop_iter =
485 entrypoint_dtor_builder().CreateAlloca(m_builder->getIntNTy(ptr_bitsize()), 0, nullptr, "sl.dtor.loop.i");
486 m_builder->CreateStore(m_builder->getIntN(ptr_bitsize(), 0), loop_iter);
487 auto* loop_entry = m_builder->GetInsertBlock();
488 auto* loop_cont = loop_entry->splitBasicBlock(m_builder->GetInsertPoint(), "sl.dtor.cont");
489 auto* loop_block = llvm::BasicBlock::Create(*m_context, "sl.dtor.loop", loop_cont->getParent(), loop_cont);
490 auto saved_insert_point = llvm::IRBuilderBase::InsertPoint{loop_cont, loop_cont->begin()};
491
492 loop_entry->getTerminator()->eraseFromParent();
493 m_builder->SetInsertPoint(loop_entry);
494 m_builder->CreateBr(loop_block);
495 m_builder->SetInsertPoint(loop_block);
496 auto* iter_i = m_builder->CreateLoad(m_builder->getIntNTy(ptr_bitsize()), loop_iter, "sl.dtor.i.iter");
497 Val iter_val = m_builder->CreateLoad(
498 base_llvm_type, m_builder->CreateInBoundsGEP(base_llvm_type, ptr, iter_i, "sl.dtor.i.gep"), "sl.dtor.i.load");
499 destruct(iter_val, base_type);
500 auto* iter_inc = m_builder->CreateAdd(iter_i, m_builder->getIntN(ptr_bitsize(), 1), "sl.dtor.i.inc");
501 m_builder->CreateStore(iter_inc, loop_iter);
502 m_builder->CreateCondBr(m_builder->CreateICmpEQ(iter_inc, size), loop_cont, loop_block);
503
504 m_builder->restoreIP(saved_insert_point);
505 }
506 create_free(ptr);
507 return;
508 }
509 if (const auto* struct_type = type.base_dyn_cast<ty::Struct>(); struct_type != nullptr) {
510 for (const auto& i : llvm::enumerate(struct_type->fields())) {
511 ty::Type type = i.value()->type->ensure_ty();
512 if (!type.is_trivially_destructible())
513 destruct(m_builder->CreateExtractValue(val, i.index(), "dt.field"), type);
514 }
515 }
516}
517
519 if (type.is_mut())
520 throw std::runtime_error("Cannot default-initialize a reference");
521 if (const auto* int_type = type.base_dyn_cast<ty::Int>())
522 return m_builder->getIntN(int_type->size(), 0);
523 if (const auto* ptr_type = type.base_dyn_cast<ty::Ptr>()) {
524 switch (ptr_type->qualifier()) {
525 default: llvm_unreachable("Ptr type cannot hold this qualifier");
526 case Qualifier::Ptr:
527 llvm::ConstantPointerNull::get(llvm::PointerType::getUnqual(llvm_type(ptr_type->pointee())));
528 break;
529 }
530 }
531 if (const auto* struct_type = type.base_dyn_cast<ty::Struct>()) {
532 auto* llvm_ty = cast<llvm::StructType>(llvm_type(type));
533 Val val = llvm::UndefValue::get(llvm_ty);
534
535 for (const auto& i : llvm::enumerate(struct_type->fields()))
536 val = m_builder->CreateInsertValue(val, default_init(i.value()->type->ensure_ty()), i.index());
537
538 return val;
539 }
540
541 throw std::runtime_error("Cannot default-initialize "s + type.name());
542}
543
544auto Compiler::declare(Fn& fn) -> llvm::Function* {
545 if (fn.llvm != nullptr)
546 return fn.llvm;
547 if (fn.primitive())
548 return nullptr;
549
550 YUME_ASSERT(fn.fn_ty != nullptr, "Function declaration "s + fn.name() + " has no function type set");
551 (void)llvm_type(fn.fn_ty); // Ensure we've created a function type
552 llvm::FunctionType* fn_t = fn.fn_ty->fn_memo();
553
554 string name = fn.name();
555 if (!fn.extern_linkage() && !fn.local())
556 name = mangle::mangle_name(fn);
557
558 auto linkage = fn.extern_linkage() ? llvm::Function::ExternalLinkage
559 : fn.local() ? llvm::Function::PrivateLinkage
560 : llvm::Function::InternalLinkage;
561 auto* llvm_fn = llvm::Function::Create(fn_t, linkage, name, m_module.get());
562 if (fn.has_annotation("interrupt") && fn.fn_ty->closure_memo() == nullptr) // HACK
563 llvm_fn->setCallingConv(llvm::CallingConv::X86_INTR);
564 fn.llvm = llvm_fn;
565
566 auto arg_names = fn.arg_names();
567 if (fn.fn_ty->closure_memo() != nullptr)
568 arg_names.insert(arg_names.begin(), "<closure>"); // For functions with closures, it is the first argument
569 for (auto [llvm_arg, arg_name] : llvm::zip(llvm_fn->args(), arg_names))
570 llvm_arg.setName("arg."s + arg_name);
571
572 // At this point, the function prototype is declared, but not the body.
573 // In the case of extern or local functions, a prototype is all that will be declared.
574 if (!fn.extern_decl() && !fn.local()) {
575 auto* fn_unit = m_source_mapping.at(fn.member);
576 auto* fn_file = m_debug->createFile(fn_unit->getFilename(), fn_unit->getDirectory());
577 llvm::DIScope* f_context = fn_file;
578 unsigned line_no = fn.ast().location().begin_line;
579 unsigned scope_line = line_no;
580 auto types = m_debug->getOrCreateTypeArray({}); // TODO(rymiel)
581 llvm::DISubprogram* subprogram =
582 m_debug->createFunction(f_context, fn.name(), name, fn_file, line_no, m_debug->createSubroutineType(types),
583 scope_line, llvm::DINode::FlagPrototyped, llvm::DISubprogram::SPFlagDefinition);
584 fn.llvm->setSubprogram(subprogram);
585
586 m_decl_queue.emplace(&fn);
587 m_walker->current_decl = &fn;
588 m_walker->body_statement(fn.ast());
589 }
590 return llvm_fn;
591}
592
593template <> void Compiler::statement(ast::Compound& stat) {
594 auto guard = m_scope.push_scope_guarded();
595 for (auto& i : stat)
596 body_statement(*i);
597
598 if (m_builder->GetInsertBlock()->getTerminator() == nullptr)
599 destruct_last_scope();
600}
601
602static void destruct_indirect(Compiler& compiler, const InScope& v) {
603 const auto ty = v.ast.ensure_ty();
604 if (v.owning && !ty.is_trivially_destructible()) {
605 YUME_ASSERT(v.value.llvm->getType() == compiler.llvm_type(ty.without_mut())->getPointerTo(),
606 "Value tracked in scope must be indirect");
607 Val llvm_val = v.value;
608 if (!ty.is_mut())
609 llvm_val = compiler.builder()->CreateLoad(compiler.llvm_type(ty), v.value, "dt.l");
610 compiler.destruct(llvm_val, ty);
611 }
612}
613
614void Compiler::destruct_last_scope() {
615 for (const auto& i : m_scope.last_scope())
616 destruct_indirect(*this, i.second);
617}
618
619void Compiler::destruct_all_scopes() {
620 for (const auto& scope : llvm::reverse(llvm::drop_begin(m_scope.all_scopes())))
621 for (const auto& i : scope)
622 destruct_indirect(*this, i.second);
623}
624
625void Compiler::expose_parameter_as_local(ty::Type type, const string& name, const ast::AST& ast, Val val) {
626 if (type.is_mut()) {
627 m_scope.add(name, {.value = val, .ast = ast, .owning = false}); // We don't own parameters
628 return;
629 }
630 Val alloc = m_builder->CreateAlloca(llvm_type(type), nullptr, "lv."s + name);
631 m_builder->CreateStore(val, alloc);
632 m_scope.add(name, {.value = alloc, .ast = ast, .owning = false}); // We don't own parameters
633}
634
635void Compiler::setup_fn_base(Fn& fn) {
636 m_current_fn = &fn;
637 m_scope.push_scope();
638 m_scope_ctor.reset();
639 auto* bb = llvm::BasicBlock::Create(*m_context, "entry", fn.llvm);
640 m_builder->SetInsertPoint(bb);
641 m_builder->SetCurrentDebugLocation({});
642
643 if (fn.abstract())
644 return; // Abstract methods don't have a real body and thus have no need to meaningfully use params as locals
645
646 // If this function has a closure, it is the first parameter and will be skipped below
647 auto arg_offset = fn.fn_ty->closure_memo() == nullptr ? 0 : 1;
648 auto llvm_fn_args = llvm::drop_begin(fn.llvm->args(), arg_offset);
649 // Allocate local variables for each parameter
650 for (auto [arg, ast_arg] : llvm::zip(llvm_fn_args, fn.args()))
651 expose_parameter_as_local(ast_arg.type, ast_arg.name, ast_arg.ast, &arg);
652}
653
655 if (cn.llvm->hasInitializer())
656 return;
657
658 auto* saved_insert_block = m_builder->GetInsertBlock();
659 m_builder->SetInsertPoint(&m_global_ctor_fn->getEntryBlock(), m_global_ctor_fn->getEntryBlock().begin());
660
661 auto init = body_expression(*cn.ast().init);
662 if ((init.scope != nullptr) && init.scope->owning)
663 init.scope->owning = false;
664
665 if (isa<llvm::Constant>(init.llvm)) {
666 cn.llvm->setConstant(true);
667 auto* const_val = cast<llvm::Constant>(init.llvm);
668 cn.llvm->setInitializer(const_val);
669 } else {
670 cn.llvm->setInitializer(llvm::ConstantAggregateZero::get(cn.llvm->getValueType()));
671 m_builder->CreateStore(init.llvm, cn.llvm);
672 }
673
674 auto cn_type = cn.ast().type->ensure_ty();
675 if (!cn_type.is_trivially_destructible()) {
676 m_builder->SetInsertPoint(&m_global_dtor_fn->getEntryBlock(), m_global_dtor_fn->getEntryBlock().begin());
677 destruct(m_builder->CreateLoad(llvm_type(cn_type), cn.llvm), cn_type);
678 }
679
680 m_builder->SetInsertPoint(saved_insert_block);
681}
682
684 setup_fn_base(fn);
685
686 if (fn.abstract()) {
687 YUME_ASSERT(fn.self_ty.has_value(), "Abstract function must refer to a self type");
688 YUME_ASSERT(fn.self_ty->base_isa<ty::Struct>(), "Abstract function must be within a struct");
689 const auto* interface = fn.self_ty->base_cast<ty::Struct>()->decl();
690 YUME_ASSERT(interface != nullptr, "Struct not found from struct type?");
691 YUME_ASSERT(interface->ast().is_interface, "Abstract function must be within interface");
692
693 auto vtable_match = std::ranges::find(interface->vtable_members, vtable_entry_for(fn));
694 YUME_ASSERT(vtable_match != interface->vtable_members.end(), "abstract method not found in vtable?");
695 auto vtable_entry_index = std::distance(interface->vtable_members.begin(), vtable_match);
696
697 auto vtable_deref_args = vector<llvm::Type*>();
698 auto* vtable_deref_ret = m_builder->getVoidTy();
699 for (const auto& arg : vtable_match->args)
700 vtable_deref_args.emplace_back(llvm_type(arg, true));
701 if (auto ret = vtable_match->ret; ret.has_value())
702 vtable_deref_ret = llvm_type(*ret, true);
703
704 auto call_args = vector<llvm::Value*>();
705 llvm::FunctionType* call_fn_type = nullptr;
706
707 // An empty arg name means it is actually fake and not in impl methods. this is a HACK
708 if (fn.arg_count() == 1 && fn.arg_names().at(0).empty()) {
709 call_fn_type = llvm::FunctionType::get(vtable_deref_ret, {}, false);
710 } else {
711 call_args.push_back(m_builder->CreateExtractValue(fn.llvm->args().begin(), 1, "abs.self"));
712 for (auto& extra_arg : llvm::drop_begin(fn.llvm->args()))
713 call_args.emplace_back(&extra_arg);
714
715 call_fn_type = llvm::FunctionType::get(vtable_deref_ret, vtable_deref_args, false);
716 }
717
718 // TODO(LLVM MIN >= 15): A lot of bit-twiddling obsoleted by opaque pointers
719
720 auto* vtable_struct_ptr = m_builder->CreateExtractValue(fn.llvm->args().begin(), 0, "abs.vt.struct");
721 auto* vtable_ptr_array =
722 m_builder->CreateBitCast(vtable_struct_ptr, m_builder->getInt8PtrTy()->getPointerTo(), "abs.vt.array");
723 auto* vtable_entry = m_builder->CreateLoad(
724 m_builder->getInt8PtrTy(),
725 m_builder->CreateConstGEP1_32(m_builder->getInt8PtrTy(), vtable_ptr_array, vtable_entry_index, "abs.vt.entry"),
726 "abs.vt.entry.load");
727 Val call_result = m_builder->CreateCall(
728 call_fn_type, m_builder->CreateBitCast(vtable_entry, call_fn_type->getPointerTo(), "abs.fn"), call_args);
729
730 if (call_result.llvm->getType()->isVoidTy())
731 m_builder->CreateRetVoid();
732 else
733 m_builder->CreateRet(call_result);
734 } else if (isa<ast::FnDecl>(&fn.ast())) {
735 if (auto* body = get_if<ast::Compound>(&fn.fn_body()); body != nullptr)
736 statement(*body);
737
738 if (m_builder->GetInsertBlock()->getTerminator() == nullptr) {
739 destruct_all_scopes();
740 m_builder->CreateRetVoid();
741 }
742 } else {
743 YUME_ASSERT(fn.self_ty.has_value(), "Cannot define constructor when the type being constructed is unknown");
744 auto* ctor_type = llvm_type(*fn.self_ty);
745 const Val base_value = llvm::UndefValue::get(ctor_type);
746 auto* base_alloc = m_builder->CreateAlloca(ctor_type, nullptr, "ctor.base");
747 m_builder->CreateStore(base_value, base_alloc);
748
749 // Act as if we don't own the object being constructed so it won't get destructed at the end of scope
750 m_scope_ctor.emplace(InScope{.value = base_alloc, .ast = fn.ast(), .owning = false});
751
752 statement(fn.compound_body());
753
754 destruct_all_scopes();
755 auto* finalized_value = m_builder->CreateLoad(ctor_type, base_alloc);
756 m_builder->CreateRet(finalized_value);
757 }
758
759 m_scope.pop_scope();
760 YUME_ASSERT(m_scope.size() == 1, "End of function should end with only the global scope remaining");
761 // llvm::verifyFunction(*fn.llvm, &errs());
762}
763
764template <> void Compiler::statement(ast::WhileStmt& stat) {
765 auto* test_bb = llvm::BasicBlock::Create(*m_context, "while.test", m_current_fn->llvm);
766 auto* head_bb = llvm::BasicBlock::Create(*m_context, "while.head", m_current_fn->llvm);
767 auto* merge_bb = llvm::BasicBlock::Create(*m_context, "while.merge", m_current_fn->llvm);
768 m_builder->CreateBr(test_bb);
769 m_builder->SetInsertPoint(test_bb);
770 auto cond_value = body_expression(*stat.cond);
771 m_builder->CreateCondBr(cond_value, head_bb, merge_bb);
772 m_builder->SetInsertPoint(head_bb);
773 body_statement(stat.body);
774 m_builder->CreateBr(test_bb);
775 m_builder->SetInsertPoint(merge_bb);
776}
777
778template <> void Compiler::statement(ast::IfStmt& stat) {
779 auto* merge_bb = llvm::BasicBlock::Create(*m_context, "if.cont", m_current_fn->llvm);
780 auto* next_test_bb = llvm::BasicBlock::Create(*m_context, "if.test", m_current_fn->llvm, merge_bb);
781 auto* last_branch = m_builder->CreateBr(next_test_bb);
782 bool all_terminated = true;
783
784 auto& clauses = stat.clauses;
785 for (auto& clause : clauses) {
786 m_builder->SetInsertPoint(next_test_bb);
787 auto* body_bb = llvm::BasicBlock::Create(*m_context, "if.then", m_current_fn->llvm, merge_bb);
788 next_test_bb = llvm::BasicBlock::Create(*m_context, "if.test", m_current_fn->llvm, merge_bb);
789 auto condition = body_expression(*clause.cond);
790 last_branch = m_builder->CreateCondBr(condition, body_bb, next_test_bb);
791 m_builder->SetInsertPoint(body_bb);
792 statement(clause.body);
793 // This has to use GetInsertBlock() since the statement being created above can introduce many new blocks.
794 if (m_builder->GetInsertBlock()->getTerminator() == nullptr) {
795 all_terminated = false;
796 m_builder->CreateBr(merge_bb);
797 }
798 }
799
800 auto& else_clause = stat.else_clause;
801 if (else_clause.has_value()) {
802 next_test_bb->setName("if.else");
803 m_builder->SetInsertPoint(next_test_bb);
804 statement(*else_clause);
805 if (m_builder->GetInsertBlock()->getTerminator() == nullptr) {
806 all_terminated = false;
807 m_builder->CreateBr(merge_bb);
808 }
809 } else {
810 // We can't consider all branches to terminate when there is no else clause, as the whole if statement could be
811 // skipped
812 all_terminated = false;
813 last_branch->setSuccessor(1, merge_bb);
814 next_test_bb->eraseFromParent();
815 }
816
817 if (all_terminated)
818 merge_bb->eraseFromParent();
819 else
820 m_builder->SetInsertPoint(merge_bb);
821}
822
823template <> void Compiler::statement(ast::ReturnStmt& stat) {
824 InScope* reset_owning = nullptr;
825
826 if (stat.expr.has_value()) {
827 // Returning a local variable also gives up ownership of it
828 if (stat.extends_lifetime != nullptr) {
829 for (auto& i : m_scope.last_scope()) {
830 auto& v = i.second;
831 if (&v.ast == stat.extends_lifetime) {
832 v.owning = false;
833 reset_owning = &v;
834 break;
835 }
836 }
837 }
838
839 m_return_value = stat.expr.raw_ptr(); // TODO(rymiel): Find a better solution than this global variable
840 auto val = body_expression(*stat.expr);
841
842 if (val.scope != nullptr && val.scope->owning)
843 val.scope->owning = false;
844
845 destruct_all_scopes();
846
847 if (reset_owning != nullptr)
848 reset_owning->owning = true; // The local variable may not be returned in all code paths, so reset its ownership
849
850 if (val.llvm->getType()->isVoidTy() || m_current_fn->llvm->getReturnType()->isVoidTy())
851 m_builder->CreateRetVoid();
852 else
853 m_builder->CreateRet(val);
854
855 return;
856 }
857
858 destruct_all_scopes();
859 m_builder->CreateRetVoid();
860}
861
862auto Compiler::entrypoint_builder() -> llvm::IRBuilder<> {
863 if (m_current_fn == nullptr)
864 return {&m_global_ctor_fn->getEntryBlock(), m_global_ctor_fn->getEntryBlock().begin()};
865 return {&m_current_fn->llvm->getEntryBlock(), m_current_fn->llvm->getEntryBlock().begin()};
866}
867
868auto Compiler::entrypoint_dtor_builder() -> llvm::IRBuilder<> {
869 if (m_current_fn == nullptr)
870 return {&m_global_dtor_fn->getEntryBlock(), m_global_dtor_fn->getEntryBlock().begin()};
871 return {&m_current_fn->llvm->getEntryBlock(), m_current_fn->llvm->getEntryBlock().begin()};
872}
873
874template <> void Compiler::statement(ast::VarDecl& stat) {
875 // Locals are currently always mut, get the base type instead
876 // TODO(rymiel): revisit, probably extract logic
877 auto* var_type = llvm_type(stat.ensure_ty().ensure_mut_base());
878
879 if (stat.init->ensure_ty().is_mut()) {
880 auto expr_val = body_expression(*stat.init);
881 m_scope.add(stat.name, {.value = expr_val, .ast = stat, .owning = false});
882 return;
883 }
884
885 auto* alloc = entrypoint_builder().CreateAlloca(var_type, nullptr, "vdecl."s + stat.name);
886
887 auto expr_val = body_expression(*stat.init);
888
889 if (expr_val.scope != nullptr && expr_val.scope->owning)
890 expr_val.scope->owning = false;
891
892 m_builder->CreateStore(expr_val, alloc);
893 m_scope.add(stat.name, {.value = alloc, .ast = stat, .owning = true});
894}
895
896template <> auto Compiler::expression(ast::NumberExpr& expr) -> Val {
897 auto val = expr.val;
898 if (expr.ensure_ty().base() == m_types.int64().s_ty)
899 return m_builder->getInt64(val);
900 return m_builder->getInt32(val);
901}
902
903template <> auto Compiler::expression(ast::CharExpr& expr) -> Val { return m_builder->getInt8(expr.val); }
904
905template <> auto Compiler::expression(ast::BoolExpr& expr) -> Val { return m_builder->getInt1(expr.val); }
906
907void Compiler::make_temporary_in_scope(Val& val, const ast::AST& ast, const string& name) {
908 const string tmp_name = name + " " + ast.location().to_string();
909 auto* md_node =
910 llvm::MDNode::get(*m_context, llvm::MDString::get(*m_context, std::to_string(m_scope.size()) + ": " + tmp_name));
911
912 auto ast_ty = ast.ensure_ty();
913 YUME_ASSERT(llvm_type(ast_ty) == val.llvm->getType(), "Temporary value type must match type in scope");
914 auto* val_ty = llvm_type(ast_ty.without_mut());
915 Val ptr = nullptr;
916 if (m_scope.size() > 1) {
917 auto* alloc = entrypoint_builder().CreateAlloca(val_ty, nullptr, name);
918 alloc->setMetadata("yume.tmp", md_node);
919 ptr = alloc;
920 } else {
921 auto* global = new llvm::GlobalVariable(*m_module, val_ty, false, llvm::GlobalVariable::PrivateLinkage,
922 llvm::ConstantAggregateZero::get(val_ty), name);
923 global->setMetadata("yume.tmp", md_node);
924 ptr = global;
925 }
926
927 auto [iter, ok] = m_scope.add(tmp_name, {.value = ptr.llvm, .ast = ast, .owning = true});
928 m_builder->CreateStore(ast_ty.is_mut() ? m_builder->CreateLoad(val_ty, val.llvm) : val.llvm, ptr.llvm);
929 val.scope = &iter->second;
930 val.scope->value = ptr;
931}
932
933template <> auto Compiler::expression(ast::StringExpr& expr) -> Val {
934 auto val = expr.val;
935
936 vector<llvm::Constant*> chars(val.length());
937 for (unsigned int i = 0; i < val.size(); i++)
938 chars[i] = m_builder->getInt8(val[i]);
939
940 auto* base_type = m_builder->getInt8Ty();
941 auto* global_string_type = llvm::ArrayType::get(base_type, chars.size());
942 auto* global_init = llvm::ConstantArray::get(global_string_type, chars);
943 auto* global = new llvm::GlobalVariable(*m_module, global_string_type, true, llvm::GlobalVariable::PrivateLinkage,
944 global_init, ".str");
945
946 auto* global_string_ptr = llvm::ConstantExpr::getBitCast(global, m_builder->getInt8PtrTy(0));
947
948 auto* slice_size = m_builder->getIntN(ptr_bitsize(), val.length());
949 auto* slice_type = cast<llvm::StructType>(llvm_type(expr.ensure_ty()));
950
951 const Val string_alloc = create_malloc(base_type, slice_size, "str.ctor.malloc");
952 m_builder->CreateMemCpy(string_alloc, {}, global_string_ptr, {}, slice_size);
953
954 Val string_slice = llvm::UndefValue::get(slice_type);
955 string_slice = m_builder->CreateInsertValue(string_slice, string_alloc, 0);
956 string_slice = m_builder->CreateInsertValue(string_slice, slice_size, 1);
957
958 make_temporary_in_scope(string_slice, expr, "tmps");
959
960 return string_slice;
961}
962
963template <> auto Compiler::expression(ast::VarExpr& expr) -> Val {
964 auto* in_scope = m_scope.find(expr.name);
965 YUME_ASSERT(in_scope != nullptr, "Variable "s + expr.name + " is not in scope");
966 auto* val = in_scope->value.llvm;
967 // Function arguments act as locals, but they are immutable, but still behind a reference (alloca)
968 if (!in_scope->ast.ensure_ty().is_mut())
969 return {m_builder->CreateLoad(llvm_type(in_scope->ast.ensure_ty()), val), in_scope};
970
971 return {val, in_scope};
972}
973
974template <> auto Compiler::expression(ast::ConstExpr& expr) -> Val {
975 for (const auto& cn : m_consts)
976 if (cn.referred_to_by(expr))
977 return m_builder->CreateLoad(llvm_type(cn.ast().ensure_ty()), cn.llvm, "cn." + expr.name);
978
979 throw std::runtime_error("Nonexistent constant called "s + expr.name);
980}
981
982/// A constexpr-friendly simple string hash, for simple switches with string cases
983static auto constexpr const_hash(const char* input) -> unsigned {
984 return *input != 0 ? static_cast<unsigned int>(*input) + 33 * const_hash(input + 1) : 5381; // NOLINT
985}
986
987auto Compiler::int_bin_primitive(const string& primitive, const vector<Val>& args) -> Val {
988 const auto& a = args.at(0);
989 const auto& b = args.at(1);
990 auto hash = const_hash(primitive.data());
991 switch (hash) {
992 case const_hash("ib_icmp_sgt"): return m_builder->CreateICmpSGT(a, b);
993 case const_hash("ib_icmp_ugt"): return m_builder->CreateICmpUGT(a, b);
994 case const_hash("ib_icmp_slt"): return m_builder->CreateICmpSLT(a, b);
995 case const_hash("ib_icmp_ult"): return m_builder->CreateICmpULT(a, b);
996 case const_hash("ib_icmp_eq"): return m_builder->CreateICmpEQ(a, b);
997 case const_hash("ib_icmp_ne"): return m_builder->CreateICmpNE(a, b);
998 case const_hash("ib_add"): return m_builder->CreateAdd(a, b);
999 case const_hash("ib_sub"): return m_builder->CreateSub(a, b);
1000 case const_hash("ib_mul"): return m_builder->CreateMul(a, b);
1001 case const_hash("ib_and"): return m_builder->CreateAnd(a, b);
1002 case const_hash("ib_srem"): return m_builder->CreateSRem(a, b);
1003 case const_hash("ib_urem"): return m_builder->CreateURem(a, b);
1004 case const_hash("ib_sdiv"): return m_builder->CreateSDiv(a, b);
1005 case const_hash("ib_udiv"): return m_builder->CreateUDiv(a, b);
1006 case const_hash("ib_shl"): return m_builder->CreateShl(a, b);
1007 case const_hash("ib_lshr"): return m_builder->CreateLShr(a, b);
1008 case const_hash("ib_ashr"): return m_builder->CreateAShr(a, b);
1009 default: throw std::runtime_error("Unknown binary integer primitive "s + primitive);
1010 }
1011}
1012
1013static inline auto vals_to_llvm(const vector<Val>& in) -> vector<llvm::Value*> {
1014 auto out = vector<llvm::Value*>{};
1015 for (const auto& i : in)
1016 out.push_back(i.llvm);
1017
1018 return out;
1019}
1020
1021auto Compiler::primitive(Fn* fn, const vector<Val>& args, const vector<ty::Type>& types,
1022 vector<ast::AnyExpr*>& ast_args) -> optional<Val> {
1023 if (fn->extern_decl())
1024 return m_builder->CreateCall(declare(*fn), vals_to_llvm(args));
1025
1026 if (!fn->primitive())
1027 return {};
1028
1029 auto primitive = get<string>(fn->fn_body());
1030
1031 if (primitive == "ptrto")
1032 return args.at(0);
1033 if (primitive == "slice_malloc") {
1034 auto base_ty_type = types.at(0).ensure_ptr_base();
1035 auto* base_type = llvm_type(base_ty_type);
1036 auto slice_size = args.at(1);
1037
1038 return create_malloc(base_type, slice_size, "sl.ctor.malloc");
1039 }
1040 if (primitive == "default_init") {
1041 auto base_type = types.at(0).ensure_mut_base();
1042 m_builder->CreateStore(default_init(base_type), args.at(0));
1043
1044 return args.at(0);
1045 }
1046 if (primitive == "set_at") {
1047 auto* result_type = llvm_type(types[0].without_mut().ensure_ptr_base());
1048 llvm::Value* value = args.at(2);
1049 llvm::Value* base = m_builder->CreateGEP(result_type, args.at(0).llvm, args.at(1).llvm, "p.set_at.gep");
1050 m_builder->CreateStore(value, base);
1051 return llvm::UndefValue::get(m_builder->getVoidTy());
1052 }
1053 if (primitive == "get_at") {
1054 auto* result_type = llvm_type(types[0].without_mut().ensure_ptr_base());
1055 llvm::Value* base = args.at(0);
1056 base = m_builder->CreateGEP(result_type, base, args.at(1).llvm, "p.get_at.gep");
1057 return base;
1058 }
1059 if (primitive == "ptr_cast")
1060 return m_builder->CreateBitCast(args[0], llvm_type(types.at(1).without_meta()), "builtin.ptr_cast");
1061 if (primitive == "ptr_gep") {
1062 return m_builder->CreateGEP(llvm_type(types.at(0).ensure_ptr_base()), args.at(0), args.at(1).llvm,
1063 "builtin.ptr_gep");
1064 }
1065 if (primitive == "cast") {
1066 // TODO(rymiel): This is an "explicit" cast, and should be able to cast more things when compared to an implicit one
1067 auto* base = ast_args.at(0);
1068 semantic::make_implicit_conversion(*base, types.at(1).without_meta());
1069 return body_expression(**base);
1070 }
1071 if (primitive.starts_with("ib_"))
1072 return int_bin_primitive(primitive, args);
1073 throw std::runtime_error("Unknown primitive "s + primitive);
1074}
1075
1076template <> auto Compiler::expression(ast::CallExpr& expr) -> Val {
1077 if (expr.name == "->") // TODO(rymiel): Magic value?
1078 return direct_call_operator(expr);
1079
1080 auto* selected = expr.selected_overload;
1081 llvm::Function* llvm_fn = nullptr;
1082
1083 vector<ast::AnyExpr*> ast_args{};
1084 vector<ty::Type> arg_types{};
1085 vector<Val> llvm_args{};
1086
1087 for (auto& i : expr.args) {
1088 auto arg = body_expression(*i);
1089 ast_args.push_back(&i);
1090 llvm_args.emplace_back(arg.llvm);
1091 arg_types.push_back(i->ensure_ty());
1092 }
1093
1094 Val val{nullptr};
1095
1096 auto prim = primitive(selected, llvm_args, arg_types, ast_args);
1097 if (prim.has_value()) {
1098 val = *prim;
1099 } else {
1100 llvm_fn = declare(*selected);
1101 val = m_builder->CreateCall(llvm_fn, vals_to_llvm(llvm_args));
1102 }
1103
1104 if (auto ty = expr.val_ty(); ty.has_value() && !ty->is_trivially_destructible())
1105 make_temporary_in_scope(val, expr, "tmp");
1106
1107 return val;
1108}
1109
1110template <> auto Compiler::expression(ast::BinaryLogicExpr& expr) -> Val {
1111 bool is_and = expr.operation == "&&"_a;
1112 string label_base = (is_and ? "land" : "lor");
1113 auto* entry_block = m_builder->GetInsertBlock();
1114 auto* rhs_block = llvm::BasicBlock::Create(*m_context, label_base + ".rhs", m_current_fn->llvm);
1115 auto* pass_block = llvm::BasicBlock::Create(*m_context, label_base + ".pass", m_current_fn->llvm);
1116 auto lhs_val = body_expression(*expr.lhs);
1117 if (is_and)
1118 m_builder->CreateCondBr(lhs_val, rhs_block, pass_block);
1119 else
1120 m_builder->CreateCondBr(lhs_val, pass_block, rhs_block);
1121 m_builder->SetInsertPoint(rhs_block);
1122 auto rhs_val = body_expression(*expr.rhs);
1123 m_builder->CreateBr(pass_block);
1124 m_builder->SetInsertPoint(pass_block);
1125 auto* phi = m_builder->CreatePHI(llvm_type(expr.ensure_ty()), 2, label_base + ".phi");
1126 if (is_and)
1127 phi->addIncoming(m_builder->getFalse(), entry_block);
1128 else
1129 phi->addIncoming(m_builder->getTrue(), entry_block);
1130 phi->addIncoming(rhs_val, rhs_block);
1131 return phi;
1132}
1133
1134template <> auto Compiler::expression(ast::AssignExpr& expr) -> Val {
1135 // TODO(rymiel): The two branches here handle assigning to variable or fields. In reality, these have a lot of
1136 // logic in common, such as to do with tracking ownership, which should be shared instead of being duplicated
1137 // across both branches.
1138 if (const auto* target_var = dyn_cast<ast::VarExpr>(expr.target.raw_ptr())) {
1139 auto* in_scope = m_scope.find(target_var->name);
1140 YUME_ASSERT(in_scope != nullptr, "Variable "s + target_var->name + " is not in scope");
1141
1142 destruct_indirect(*this, *in_scope);
1143
1144 auto expr_val = body_expression(*expr.value);
1145 if (expr_val.scope != nullptr && expr_val.scope->owning)
1146 expr_val.scope->owning = false;
1147
1148 m_builder->CreateStore(expr_val, in_scope->value.llvm);
1149 return expr_val;
1150 }
1151 if (auto* field_access = dyn_cast<ast::FieldAccessExpr>(expr.target.raw_ptr())) {
1152 auto& field_base = field_access->base;
1153 const auto [struct_base, base] = [&]() -> tuple<ty::Type, Val> {
1154 if (field_base.has_value()) {
1155 auto base = body_expression(*field_base);
1156 auto struct_base = field_access->base->ensure_ty();
1157 return {struct_base, base};
1158 } // TODO(rymiel): revisit
1159 if (!isa<ast::CtorDecl>(m_current_fn->ast()))
1160 throw std::logic_error("Field access without a base is only available in constructors");
1161
1162 auto [value, ast, owning] = *m_scope_ctor;
1163 return {ast.ensure_ty().known_mut(), value};
1164 }();
1165
1166 YUME_ASSERT(struct_base.is_mut(), "Cannot assign into field of immutable structure");
1167
1168 const string base_name = field_access->field;
1169 const int base_offset = field_access->offset;
1170
1171 auto expr_val = body_expression(*expr.value);
1172
1173 if (expr_val.scope != nullptr && expr_val.scope->owning) {
1174 expr_val.scope->owning = false;
1175 } else if (!expr.value->ensure_ty().is_trivially_destructible() && expr_val.scope != nullptr &&
1176 !expr_val.scope->owning) {
1177 make_dup(expr_val, expr.value);
1178 }
1179 const auto* struct_type = struct_base.ensure_mut_base().base_cast<ty::Struct>();
1180
1181 YUME_ASSERT(base_offset >= 0, "Field access has unknown offset into struct");
1182
1183 auto* gep = m_builder->CreateStructGEP(llvm_type(struct_type), base, base_offset, "s.sf."s + base_name);
1184
1185 // This check is in place to not attempt to destruct fields when they are being constructed.
1186 // TODO(rymiel): Replace this naive check with some sort of "first definite assignment" analysis
1187 if (field_base.has_value() && !expr.value->ensure_ty().is_trivially_destructible()) {
1188 auto field_type = struct_type->m_fields.at(base_offset)->type->ensure_ty();
1189 destruct(m_builder->CreateLoad(llvm_type(field_type), gep), field_type);
1190 }
1191
1192 m_builder->CreateStore(expr_val, gep);
1193 return expr_val;
1194 }
1195 throw std::runtime_error("Can't assign to target "s + expr.target->kind_name());
1196}
1197
1198template <> auto Compiler::expression(ast::LambdaExpr& expr) -> Val {
1199 auto fn = Fn{&expr, m_current_fn->member, m_current_fn->self_ty, &m_current_fn->subs};
1200 fn.fn_ty = expr.ensure_ty().base_cast<ty::Function>();
1201
1202 declare(fn);
1203 expr.llvm_fn = fn.llvm;
1204
1205 Val fn_bundle = llvm::UndefValue::get(fn.fn_ty->memo());
1206 fn_bundle = m_builder->CreateInsertValue(fn_bundle, fn.llvm, 0);
1207
1208 auto* llvm_closure_ty = fn.fn_ty->closure_memo();
1209 auto* llvm_closure = m_builder->CreateAlloca(llvm_closure_ty);
1210
1211 // Capture every closured local in the closure object
1212 for (const auto& i : llvm::enumerate(llvm::zip(expr.closured_names, expr.closured_nodes))) {
1213 auto [name, ast_arg] = i.value();
1214 auto type = ast_arg->ensure_ty();
1215 auto* val = m_scope.find(name);
1216 YUME_ASSERT(val != nullptr, "Captured variable not found in outer scope");
1217 YUME_ASSERT(val->ast.ensure_ty() == type, "Capture variable does not match expected type: wanted "s + type.name() +
1218 ", but got " + val->ast.ensure_ty().name());
1219
1220 m_builder->CreateStore(val->value,
1221 m_builder->CreateConstInBoundsGEP2_32(llvm_closure_ty, llvm_closure, 0, i.index()));
1222 }
1223
1224 // TODO(LLVM MIN >= 15): BitCast obsoleted by opaque pointers
1225 fn_bundle =
1226 m_builder->CreateInsertValue(fn_bundle, m_builder->CreateBitCast(llvm_closure, m_builder->getInt8PtrTy()), 1);
1227
1228 auto saved_scope = m_scope;
1229 auto* saved_insert_point = m_builder->GetInsertBlock();
1230 auto* saved_fn = m_current_fn;
1231 auto saved_debug_location = m_builder->getCurrentDebugLocation();
1232
1233 setup_fn_base(fn);
1234
1235 // TODO(LLVM MIN >= 15): BitCast obsoleted by opaque pointers
1236 const Val closure_val = m_builder->CreateLoad(
1237 llvm_closure_ty, m_builder->CreateBitCast(fn.llvm->arg_begin(), llvm_closure_ty->getPointerTo()), "closure");
1238
1239 // Add local variables for every captured variable
1240 for (const auto& i : llvm::enumerate(llvm::zip(expr.closured_names, expr.closured_nodes))) {
1241 auto [name, ast_arg] = i.value();
1242 auto type = ast_arg->ensure_ty();
1243 const Val arg = m_builder->CreateExtractValue(closure_val, i.index());
1244 expose_parameter_as_local(type, name, *ast_arg, arg);
1245 }
1246
1247 body_statement(expr.body);
1248 if (m_builder->GetInsertBlock()->getTerminator() == nullptr)
1249 m_builder->CreateRetVoid();
1250
1251 m_scope = saved_scope;
1252 m_builder->SetInsertPoint(saved_insert_point);
1253 m_current_fn = saved_fn;
1254 m_builder->SetCurrentDebugLocation(saved_debug_location);
1255
1256 return fn_bundle;
1257}
1258
1260 YUME_ASSERT(expr.args.size() > 1, "Direct call must have at least 1 argument");
1261 auto& base_expr = *expr.args[0];
1262
1263 auto call_target_ty = base_expr.ensure_ty();
1264 YUME_ASSERT(call_target_ty.base_isa<ty::Function>(), "Direct call target must be a function type");
1265 auto* llvm_fn_ty = call_target_ty.base_cast<ty::Function>()->fn_memo();
1266 auto* llvm_fn_bundle_ty = llvm_type(call_target_ty.without_mut());
1267
1268 auto base = body_expression(base_expr);
1269 if (call_target_ty.is_mut())
1270 base = m_builder->CreateLoad(llvm_fn_bundle_ty, base.llvm, "indir.fnptr.deref");
1271
1272 auto* llvm_fn_ptr = m_builder->CreateExtractValue(base, 0, "fnptr.fn");
1273 auto* llvm_fn_closure = m_builder->CreateExtractValue(base, 1, "fnptr.closure");
1274
1275 vector<llvm::Value*> args{};
1276 args.reserve(expr.args.size() - 1);
1277 args.push_back(llvm_fn_closure);
1278
1279 for (auto& i : llvm::drop_begin(expr.args))
1280 args.push_back(body_expression(*i));
1281
1282 return m_builder->CreateCall(llvm_fn_ty, llvm_fn_ptr, args,
1283 llvm_fn_ty->getReturnType()->isVoidTy() ? "" : "indir.call");
1284}
1285
1286template <> auto Compiler::expression(ast::CtorExpr& expr) -> Val {
1287 auto type = expr.ensure_ty();
1288 if (type.without_mut().base_isa<ty::Struct>()) {
1289 // TODO(rymiel): #4 determine what kind of allocation must be done, and if at all. It'll probably require a
1290 // complicated semantic step to determine object lifetime, which would probably be evaluated before compilation of
1291 // these expressions.
1292
1293 //// Heap allocation
1294 // auto* llvm_struct_type = llvm_type(*struct_type);
1295 // llvm::Value* alloc = nullptr;
1296 // auto* alloc_size = llvm::ConstantExpr::getSizeOf(llvm_struct_type);
1297 // alloc = llvm::CallInst::CreateMalloc(m_builder->GetInsertBlock(), m_builder->getInt64Ty(), llvm_struct_type,
1298 // alloc_size, nullptr, nullptr, "s.ctor.malloc");
1299 // alloc = m_builder->Insert(alloc);
1300
1301 //// Stack allocation
1302 // alloc = m_builder->CreateAlloca(llvm_struct_type, 0, nullptr, "s.ctor.alloca");
1303
1304 //// Value allocation
1305 auto* selected_ctor_overload = expr.selected_overload;
1306
1307 auto* llvm_fn = declare(*selected_ctor_overload);
1308 vector<llvm::Value*> llvm_args{};
1309 for (auto& i : expr.args) {
1310 auto arg = body_expression(*i);
1311 llvm_args.push_back(arg.llvm);
1312 }
1313 Val base_value = m_builder->CreateCall(llvm_fn, llvm_args);
1314
1315 //// Heap allocation
1316 // if (mut) {
1317 // m_builder->CreateStore(base_value, alloc);
1318 // base_value = alloc;
1319 // }
1320
1321 return base_value;
1322 }
1323 if (auto int_type = type.without_mut().try_as<ty::Int>()) {
1324 YUME_ASSERT(expr.args.size() == 1, "Numeric cast can only contain a single argument");
1325 auto& cast_from = expr.args[0];
1326 YUME_ASSERT(cast_from->ensure_ty().without_mut().base_isa<ty::Int>(), "Numeric cast must convert from int");
1327 auto base = body_expression(*cast_from);
1328 if (cast_from->ensure_ty().without_mut().base_cast<ty::Int>()->is_signed())
1329 return m_builder->CreateSExtOrTrunc(base, llvm_type(*int_type));
1330 return m_builder->CreateZExtOrTrunc(base, llvm_type(*int_type));
1331 }
1332
1333 //// Stack allocation of slice
1334 // if (auto* const_value = dyn_cast<llvm::ConstantInt>(slice_size.llvm())) {
1335 // auto slice_size_val = const_value->getLimitedValue();
1336 // auto* array_type = ArrayType::get(base_type, slice_size_val);
1337 // auto* array_alloc = m_builder->CreateAlloca(array_type, nullptr, "sl.ctor.alloc");
1338
1339 // auto* data_ptr = m_builder->CreateBitCast(array_alloc, base_type->getPointerTo(), "sl.ctor.ptr");
1340 // llvm::Value* slice_inst = llvm::UndefValue::get(llvm_slice_type);
1341 // slice_inst = m_builder->CreateInsertValue(slice_inst, data_ptr, 0);
1342 // slice_inst = m_builder->CreateInsertValue(slice_inst, m_builder->getInt64(slice_size_val), 1);
1343 // slice_inst->setName("sl.ctor.inst");
1344
1345 // return slice_inst;
1346 // }
1347
1348 // TODO(rymiel): the commented-out block above stack-allocates a slice constructor if its size can be determined
1349 // trivially. However, since it references stack memory, a slice allocated this way could never be feasibly returned
1350 // or passed into a function which stores a reference to it, etc. The compiler currently does nothing resembling
1351 // "escape analysis", however something like that might be needed to perform an optimization like shown above.
1352
1353 // TODO(rymiel): Note that also a slice could be stack-allocated even if its size *wasn't* known at compile time,
1354 // however, I simply didn't know how to do that when i wrote the above snippet. But, since its problematic anyway,
1355 // it remains unfixed (and commented out); revisit later.
1356
1357 // TODO(rymiel): A large slice may be unfeasible to be stack-allocated anyway, so in addition to the above points,
1358 // slice size could also be a consideration. Perhaps we don't *want* to stack-allocate unknown-sized slices as they
1359 // may be absurdly huge in size and cause stack overflow.
1360 // Related: #4
1361
1362 // NOTE: The above comments are now largely irrelevant as slice allocation is sort-of performed in-source, with the
1363 // __builtin_slice_malloc primitive. For the above to apply, the compiler must more invasively track the lifetime of
1364 // slices, and skip the constructor entirely, or something like that
1365
1366 throw std::runtime_error("Can't construct non-struct, non-integer type");
1367}
1368
1369auto Compiler::ptr_bitsize() -> unsigned int { return m_module->getDataLayout().getPointerSizeInBits(); }
1370
1371auto Compiler::create_malloc(llvm::Type* base_type, Val slice_size, string_view name) -> Val {
1372 auto* size_type = m_builder->getIntNTy(ptr_bitsize());
1373 slice_size = m_builder->CreateSExtOrTrunc(slice_size, size_type);
1374 Val alloc_size = llvm::ConstantExpr::getTrunc(llvm::ConstantExpr::getSizeOf(base_type), size_type);
1375
1376 alloc_size = m_builder->CreateMul(slice_size, alloc_size, "mallocsize");
1377
1378 // prototype malloc as "void *malloc(size_t)"
1379 llvm::FunctionCallee malloc_func = m_module->getOrInsertFunction("malloc", m_builder->getInt8PtrTy(), size_type);
1380 auto* m_call = m_builder->CreateCall(malloc_func, alloc_size.llvm, "malloccall");
1381 Val result = m_builder->CreateBitCast(m_call, base_type->getPointerTo(), name);
1382
1383 m_call->setTailCall();
1384 if (auto* fn = dyn_cast<llvm::Function>(malloc_func.getCallee())) {
1385 m_call->setCallingConv(fn->getCallingConv());
1386 if (!fn->returnDoesNotAlias())
1387 fn->setReturnDoesNotAlias();
1388 }
1389
1390 return result;
1391}
1392
1393auto Compiler::create_malloc(llvm::Type* base_type, uint64_t slice_size, string_view name) -> Val {
1394 return create_malloc(base_type, m_builder->getIntN(ptr_bitsize(), slice_size), name);
1395}
1396
1397auto Compiler::create_free(Val ptr) -> Val {
1398 auto* void_ty = m_builder->getVoidTy();
1399 auto* int_ptr_ty = m_builder->getInt8PtrTy();
1400 // prototype free as "void free(void*)"
1401 llvm::FunctionCallee free_func = m_module->getOrInsertFunction("free", void_ty, int_ptr_ty);
1402 auto* result = m_builder->CreateCall(free_func, m_builder->CreateBitCast(ptr, int_ptr_ty));
1403 result->setTailCall();
1404 if (auto* fn = dyn_cast<llvm::Function>(free_func.getCallee()))
1405 result->setCallingConv(fn->getCallingConv());
1406
1407 return result;
1408}
1409
1410template <> auto Compiler::expression(ast::SliceExpr& expr) -> Val {
1411 auto* slice_size = m_builder->getIntN(ptr_bitsize(), expr.args.size());
1412
1413 YUME_ASSERT(expr.ensure_ty().is_slice(), "Slice expression must contain slice type");
1414 auto* slice_type = llvm_type(expr.ensure_ty());
1415 auto base_type = expr.ensure_ty().base_cast<ty::Struct>()->fields().at(0)->ensure_ty().ensure_ptr_base(); // ???
1416 auto* base_llvm_type = llvm_type(base_type);
1417
1418 const Val data_ptr = create_malloc(base_llvm_type, slice_size, "sl.ctor.malloc");
1419
1420 unsigned j = 0;
1421 for (auto& i : expr.args) {
1422 if (!base_type.is_trivially_destructible()) {
1423 auto dup_args = vector<ast::AnyExpr>{};
1424 dup_args.emplace_back(move(i));
1425 auto ast_dup =
1426 std::make_unique<ast::CtorExpr>(expr.token_range(), ast::AnyType{expr.type->clone()}, move(dup_args));
1427 m_walker->body_expression(*ast_dup);
1428 i = ast::AnyExpr{move(ast_dup)};
1429 }
1430 Val val = body_expression(*i);
1431 m_builder->CreateStore(val, m_builder->CreateConstInBoundsGEP1_32(base_llvm_type, data_ptr, j++));
1432 }
1433
1434 Val slice_inst = llvm::UndefValue::get(slice_type);
1435 slice_inst = m_builder->CreateInsertValue(slice_inst, data_ptr, 0);
1436 slice_inst = m_builder->CreateInsertValue(slice_inst, slice_size, 1);
1437
1438 make_temporary_in_scope(slice_inst, expr, "tmpsl");
1439
1440 return slice_inst;
1441}
1442
1443template <> auto Compiler::expression(ast::FieldAccessExpr& expr) -> Val {
1444 optional<Val> base;
1445 optional<ty::Type> base_type;
1446 if (expr.base.has_value()) {
1447 base = body_expression(*expr.base);
1448 base_type = expr.base->ensure_ty().mut_base();
1449 } else {
1450 YUME_ASSERT(m_scope_ctor.has_value(), "Cannot access field without receiver outside of a constructor");
1451 base = m_scope_ctor->value;
1452 base_type = m_current_fn->self_ty;
1453 }
1454
1455 const string base_name = expr.field;
1456 const int base_offset = expr.offset;
1457
1458 if (!expr.ensure_ty().is_mut())
1459 return m_builder->CreateExtractValue(*base, base_offset, "s.field.nm."s + base_name);
1460
1461 YUME_ASSERT(base_type.has_value(), "Cannot access field of unknown type");
1462 return m_builder->CreateStructGEP(llvm_type(base_type.value()), *base, base_offset, "s.field.m."s + base_name);
1463}
1464
1465void Compiler::make_dup(Val& value, ast::AnyExpr& ast) {
1466 auto* ast_dup = m_walker->make_dup(ast);
1467 auto* llvm_fn = declare(*ast_dup);
1468 vector<llvm::Value*> llvm_args{};
1469 llvm_args.push_back(value.llvm);
1470 value.llvm = m_builder->CreateCall(llvm_fn, llvm_args);
1471}
1472
1473auto Compiler::get_vtable(Struct& st, const Struct& iface) -> nonnull<llvm::GlobalVariable*> {
1474 if (st.vtable_memo == nullptr) {
1475 auto vtable_entries = vector<llvm::Constant*>();
1476 auto vtable_types = vector<llvm::Type*>();
1477 vtable_entries.resize(iface.vtable_members.size());
1478 vtable_types.resize(iface.vtable_members.size());
1479
1480 for (const auto& i : st.body()) {
1481 if (!isa<ast::FnDecl>(*i))
1482 continue;
1483
1484 const auto& fn_ast = cast<ast::FnDecl>(*i);
1485 auto* fn = fn_ast.sema_decl;
1486 YUME_ASSERT(fn != nullptr, "Fn not found from fn ast?");
1487
1488 auto vtable_match = std::ranges::find(iface.vtable_members, vtable_entry_for(*fn));
1489 if (vtable_match == iface.vtable_members.end())
1490 continue;
1491
1492 auto index = std::distance(iface.vtable_members.begin(), vtable_match);
1493 vtable_entries[index] = declare(*fn);
1494 vtable_types[index] = fn->fn_ty->memo();
1495 }
1496
1497 if (std::ranges::any_of(vtable_entries, [](auto* ptr) { return ptr == nullptr; })) {
1498 std::stringstream str;
1499 str << "The struct `" << st.name() << "' implements the interface `" << iface.name()
1500 << "', but does not implement an abstract method.\n";
1501 str << "These abstract methods were unimplemented:";
1502 for (const auto& i : llvm::enumerate(vtable_entries)) {
1503 if (i.value() != nullptr)
1504 continue;
1505
1506 // TODO(rymiel): Also write the types, as there can be overloads
1507 str << "\n " << iface.vtable_members.at(i.index()).name;
1508 }
1509 throw std::runtime_error(str.str());
1510 }
1511
1512 auto* vtable_struct = llvm::ConstantStruct::getAnon(vtable_entries);
1513 st.vtable_memo =
1514 new llvm::GlobalVariable(*m_module, vtable_struct->getType(), true, llvm::GlobalVariable::PrivateLinkage,
1515 vtable_struct, ".vtable." + iface.name() + "." + st.name());
1516 }
1517
1518 // The above check will always make the memoized value nonnull.
1519 return static_cast<nonnull<llvm::GlobalVariable*>>(st.vtable_memo);
1520}
1521
1522template <> auto Compiler::expression(ast::ImplicitCastExpr& expr) -> Val {
1523 auto target_ty = expr.ensure_ty();
1524 auto current_ty = expr.base->ensure_ty();
1525 Val base = body_expression(*expr.base);
1526
1527 if (expr.conversion.dereference) {
1528 // TODO(rymiel): Hack on top of a hack (see Type::compatibility)
1529 YUME_ASSERT(current_ty.is_mut() || current_ty.is_opaque_self(),
1530 "Source type must be mutable or opaque when implicitly derefencing");
1531 // TODO(rymiel): Should really just be .ensure_mut_base()
1532 current_ty = current_ty.without_mut().without_opaque();
1533 base.llvm = m_builder->CreateLoad(llvm_type(current_ty), base, "ic.deref");
1534 if (!current_ty.is_trivially_destructible() && base.scope != nullptr && base.scope->owning &&
1535 m_return_value == &expr) {
1536 make_dup(base, expr.base);
1537 }
1538 }
1539
1540 if (expr.conversion.kind == ty::Conv::None)
1541 return base;
1542 if (expr.conversion.kind == ty::Conv::Int)
1543 return m_builder->CreateIntCast(base, llvm_type(target_ty), current_ty.base_cast<ty::Int>()->is_signed(), "ic.int");
1544 if (expr.conversion.kind == ty::Conv::FnPtr) {
1545 YUME_ASSERT(current_ty.base_isa<ty::Function>(), "fnptr conversion source must be a function type");
1546 YUME_ASSERT(isa<ast::LambdaExpr>(*expr.base), "fnptr conversion source must be a lambda");
1547 YUME_ASSERT(target_ty.base_isa<ty::Function>(), "fnptr conversion target must be a function type");
1548 auto& lambda_expr = cast<ast::LambdaExpr>(*expr.base);
1549
1550 auto fn = Fn{&lambda_expr, nullptr, std::nullopt, nullptr};
1551 fn.fn_ty = target_ty.base_cast<ty::Function>();
1552
1553 declare(fn);
1554
1555 auto saved_scope = m_scope;
1556 auto* saved_insert_point = m_builder->GetInsertBlock();
1557 auto* saved_fn = m_current_fn;
1558
1559 setup_fn_base(fn);
1560
1561 body_statement(lambda_expr.body);
1562 if (m_builder->GetInsertBlock()->getTerminator() == nullptr && !fn.fn_ty->m_ret.has_value())
1563 m_builder->CreateRetVoid();
1564
1565 m_scope = saved_scope;
1566 m_builder->SetInsertPoint(saved_insert_point);
1567 m_current_fn = saved_fn;
1568
1569 return fn.llvm;
1570 }
1571 if (expr.conversion.kind == ty::Conv::Virtual) {
1572 YUME_ASSERT(target_ty.base_isa<ty::Struct>(), "Virtual cast must cast into a struct type");
1573 YUME_ASSERT(current_ty.base_isa<ty::Struct>(), "Virtual cast must cast from a struct type");
1574 const auto* target_st_ty = target_ty.base_cast<ty::Struct>();
1575 const auto* current_st_ty = current_ty.base_cast<ty::Struct>();
1576
1577 const auto* target_st = target_st_ty->decl();
1578 YUME_ASSERT(target_st != nullptr, "Struct not found from struct type?");
1579 YUME_ASSERT(target_st->ast().is_interface, "Virtual cast must cast into an interface type");
1580
1581 auto* current_st = current_st_ty->decl();
1582 YUME_ASSERT(current_st != nullptr, "Struct not found from struct type?");
1583
1584 auto* vtable = get_vtable(*current_st, *target_st);
1585
1586 Val erased_original = nullptr;
1587 if (current_ty.is_mut()) {
1588 erased_original = base;
1589 } else {
1590 erased_original = entrypoint_builder().CreateAlloca(llvm_type(current_st_ty));
1591 m_builder->CreateStore(base, erased_original);
1592 }
1593
1594 auto* interface_struct_ty = llvm::StructType::get(m_builder->getInt8PtrTy(), m_builder->getInt8PtrTy());
1595 Val interface_struct = llvm::UndefValue::get(interface_struct_ty);
1596 interface_struct =
1597 m_builder->CreateInsertValue(interface_struct, m_builder->CreateBitCast(vtable, m_builder->getInt8PtrTy()), 0);
1598 interface_struct = m_builder->CreateInsertValue(
1599 interface_struct, m_builder->CreateBitCast(erased_original, m_builder->getInt8PtrTy()), 1);
1600
1601 return interface_struct;
1602 }
1603 throw std::runtime_error("Unknown implicit conversion " + expr.conversion.to_string());
1604}
1605
1606template <> auto Compiler::expression(ast::TypeExpr& expr) -> Val {
1607 YUME_ASSERT(expr.ensure_ty().is_meta(), "Type expr must have metatype as its type");
1608 return m_builder->getInt8(0);
1609}
1610
1611void Compiler::write_object(const char* filename, bool binary) {
1612 auto dest = open_file(filename);
1613
1614 llvm::legacy::PassManager pass;
1615 auto file_type = binary ? llvm::CGFT_ObjectFile : llvm::CGFT_AssemblyFile;
1616
1617 if (m_target_machine->addPassesToEmitFile(pass, *dest, nullptr, file_type)) {
1618 errs() << "TargetMachine can't emit a file of this type";
1619 throw std::exception();
1620 }
1621
1622 pass.run(*m_module);
1623 dest->flush();
1624}
1625
1627 const ASTStackTrace guard("Codegen: "s + stat.kind_name() + " statement", stat);
1628 m_builder->SetCurrentDebugLocation({});
1629 return CRTPWalker::body_statement(stat);
1630}
1631
1633 const ASTStackTrace guard("Codegen: "s + expr.kind_name() + " expression", expr);
1634 m_builder->SetCurrentDebugLocation({});
1635 if (m_current_fn != nullptr && m_current_fn->llvm != nullptr) {
1636 if (llvm::DIScope* scope = m_current_fn->llvm->getSubprogram(); scope != nullptr) {
1637 m_builder->SetCurrentDebugLocation(
1638 llvm::DILocation::get(scope->getContext(), expr.location().begin_line, expr.location().begin_col, scope));
1639 }
1640 }
1641 return CRTPWalker::body_expression(expr);
1642}
1643} // namespace yume
The Compiler the the primary top-level type during compilation. A single instance is created during t...
Definition: compiler.hpp:45
auto builder() const -> const auto &
Definition: compiler.hpp:82
auto body_expression(ast::Expr &expr) -> Val
Definition: compiler.cpp:1632
void body_statement(ast::Stmt &)
Definition: compiler.cpp:1626
auto ptr_bitsize() -> unsigned int
Definition: compiler.cpp:1369
auto llvm_type(ty::Type type, bool erase_opaque=false) -> llvm::Type *
Convert a type into its corresponding LLVM type.
Definition: compiler.cpp:429
auto direct_call_operator(ast::CallExpr &expr) -> Val
Definition: compiler.cpp:1259
void run()
Begin compilation!
Definition: compiler.cpp:180
void define(Fn &)
Compile the body of a function or constructor.
Definition: compiler.cpp:683
void write_object(const char *filename, bool binary)
Definition: compiler.cpp:1611
auto declare(Fn &) -> llvm::Function *
Declare a function/constructor in bytecode, or get an existing declaration.
Definition: compiler.cpp:544
Compiler(const optional< string > &target_triple, vector< SourceFile > source_files)
Definition: compiler.cpp:88
auto decl_statement(ast::Stmt &, optional< ty::Type > parent=std::nullopt, ast::Program *member=nullptr, nullable< Substitutions * > parent_subs=nullptr) -> DeclLike
Definition: compiler.cpp:305
void destruct(Val val, ty::Type type)
Destructs an object val of specified type type.
Definition: compiler.cpp:472
auto default_init(ty::Type type) -> Val
Default-constructs an object of specified type type.
Definition: compiler.cpp:518
auto create_struct(Struct &) -> bool
Definition: compiler.cpp:277
All nodes in the AST tree of the program inherit from this class.
Definition: ast.hpp:224
auto kind_name() const -> string
Human-readable string representation of the Kind of this node.
Definition: ast.hpp:274
auto location() const -> Loc
The union of the locations of the Tokens making up this node.
Definition: ast.cpp:28
auto ensure_ty() const -> ty::Type
Definition: ast.hpp:254
Expressions have an associated value and type.
Definition: ast.hpp:438
auto has_value() const -> bool
Definition: ast.hpp:172
auto raw_ptr() const -> const T *
Definition: ast.hpp:173
Statements make up most things in source code.
Definition: ast.hpp:297
A function pointer type.
Definition: type.hpp:129
auto closure_memo() const -> auto *
Definition: type.hpp:163
auto fn_memo() const -> auto *
Definition: type.hpp:162
A built-in integral type, such as I32 or Bool.
Definition: type.hpp:35
auto is_signed() const -> bool
Definition: type.hpp:42
A metatype, that is, a type referring to a type.
Definition: type.hpp:198
The null type, Nil.
Definition: type.hpp:49
The "self" type of abstract or overriding functions. An extra layer of indirection is introduced for ...
Definition: type.hpp:186
A "qualified" type, with a stackable qualifier, i.e. ptr.
Definition: type.hpp:59
An user-defined struct type with associated fields.
Definition: type.hpp:78
auto decl() const -> nonnull< yume::Struct * >
Definition: type.hpp:115
auto is_interface() const -> bool
Definition: type.cpp:377
A "qualified" type, with a non-stackable qualifier, i.e. mut.
Definition: type_base.hpp:66
auto is_slice() const noexcept -> bool
Definition: type.cpp:242
auto is_mut() const noexcept -> bool
Definition: type_base.hpp:114
auto mut_base() const noexcept -> optional< Type >
If this type is a mutable reference, return the base of it (T mut -> T)
Definition: type.cpp:381
auto is_trivially_destructible() const -> bool
Definition: type.cpp:253
auto base_cast() const noexcept -> nonnull< const T * >
Definition: type_base.hpp:80
auto base_dyn_cast() const noexcept -> nullable< const T * >
Definition: type_base.hpp:81
string_view end
Definition: errors.cpp:42
AnyBase< Type > AnyType
Definition: ast.hpp:322
auto mangle_name(Fn &fn) -> string
Definition: mangle.cpp:9
void make_implicit_conversion(ast::OptionalExpr &expr, optional< ty::Type > target_ty)
Definition: type_walker.cpp:55
Definition: ast.cpp:8
static auto make_cdtor_fn(llvm::IRBuilder<> &builder, llvm::Module &module, bool is_ctor) -> llvm::Function *
Definition: compiler.cpp:62
T nullable
Definition: util.hpp:72
static auto vals_to_llvm(const vector< Val > &in) -> vector< llvm::Value * >
Definition: compiler.cpp:1013
static auto constexpr const_hash(const char *input) -> unsigned
A constexpr-friendly simple string hash, for simple switches with string cases.
Definition: compiler.cpp:983
static auto vtable_entry_for(const Fn &fn) -> VTableEntry
Definition: compiler.cpp:161
static auto build_struct_type(Compiler &compiler, const ty::Struct &type) -> llvm::Type *
Definition: compiler.cpp:411
static auto build_function_type(Compiler &compiler, const ty::Function &type) -> llvm::Type *
Definition: compiler.cpp:370
auto open_file(nonnull< const char * > filename) -> unique_ptr< llvm::raw_pwrite_stream >
Opens a writeable stream to a file with the given filename relative to the current working directory.
Definition: util.hpp:91
static void create_vtable_for(Struct &st)
Definition: compiler.cpp:165
static void destruct_indirect(Compiler &compiler, const InScope &v)
Definition: compiler.cpp:602
auto body_statement(ast::Stmt &stat, auto &&... args)
Definition: crtp_walker.hpp:26
auto body_expression(ast::Expr &expr, auto &&... args)
Definition: crtp_walker.hpp:42
A constant declaration in the compiler.
Definition: vals.hpp:174
llvm::GlobalVariable * llvm
Definition: vals.hpp:180
auto ast() const noexcept -> const auto &
Definition: vals.hpp:185
A common base between declarations in the compiler: Fn, Struct and Const. Its value may also be absen...
Definition: vals.hpp:208
A function declaration in the compiler.
Definition: vals.hpp:52
ast::Program * member
The program this declaration is a member of.
Definition: vals.hpp:59
optional< ty::Type > self_ty
If this function is in the body of a struct, this points to its type. Used for the self type.
Definition: vals.hpp:57
auto arg_names() const -> vector< string >
Definition: vals.cpp:92
auto fn_body() -> ast::FnDecl::Body &
Definition: vals.cpp:134
auto primitive() const -> bool
Definition: vals.cpp:103
auto compound_body() -> ast::Compound &
Definition: vals.cpp:128
auto local() const -> bool
Definition: vals.cpp:112
auto extern_decl() const -> bool
Definition: vals.cpp:109
auto get_self_ty() const -> optional< ty::Type >
Definition: vals.hpp:80
auto arg_types() const -> vector< ty::Type >
Definition: vals.cpp:91
llvm::Function * llvm
The LLVM function definition corresponding to this function or constructor.
Definition: vals.hpp:65
auto extern_linkage() const -> bool
Definition: vals.cpp:115
auto abstract() const -> bool
Definition: vals.cpp:106
auto ast() const -> const ast::Stmt &
Definition: vals.cpp:74
void make_extern_linkage(bool value=true)
Definition: vals.cpp:123
auto has_annotation(const string &name) const -> bool
Definition: vals.cpp:118
const ty::Function * fn_ty
Definition: vals.hpp:55
auto ret() const -> optional< ty::Type >
Definition: vals.cpp:78
auto name() const noexcept -> string
Definition: vals.cpp:64
A local variable in function scope. Used to track destructing when the scope ends.
Definition: vals.hpp:273
bool owning
Whether the local scope "owns" the variable. Unowned variables are not destructed at the end of the s...
Definition: vals.hpp:277
const ast::AST & ast
Definition: vals.hpp:275
A struct declaration in the compiler.
Definition: vals.hpp:136
auto name() const noexcept -> string
Definition: vals.cpp:68
auto has_annotation(const string &name) const -> bool
Definition: vals.hpp:165
ast::StructDecl & st_ast
Definition: vals.hpp:137
auto body() const noexcept -> const auto &
Definition: vals.hpp:160
auto get_subs() const -> const Substitutions &
Definition: vals.hpp:163
auto get_self_ty() const noexcept -> optional< ty::Type >
Definition: vals.hpp:162
auto ast() const noexcept -> const auto &
Definition: vals.hpp:158
std::vector< VTableEntry > vtable_members
Definition: vals.hpp:147
nullable< llvm::GlobalVariable * > vtable_memo
Definition: vals.hpp:148
optional< ty::Type > self_ty
The type of this struct. Used for the self type.
Definition: vals.hpp:139
ast::Program * member
The program this declaration is a member of.
Definition: vals.hpp:141
void declare_size_type(Compiler &)
Definition: type_holder.cpp:33
A value of a complied expression.
Definition: vals.hpp:262
llvm::Value * llvm
Definition: vals.hpp:263
InScope * scope
Definition: vals.hpp:264
An assignment (=).
Definition: ast.hpp:652
A logical operator such as || or &&. Since these aren't overloadable, they have their own AST node.
Definition: ast.hpp:581
Bool literals (true or false).
Definition: ast.hpp:508
A function call or operator.
Definition: ast.hpp:562
Char literals.
Definition: ast.hpp:495
A statement consisting of multiple other statements, i.e. the body of a function.
Definition: ast.hpp:712
A constant. Currently global.
Definition: ast.hpp:547
A construction of a struct or cast of a primitive.
Definition: ast.hpp:597
Direct access of a field of a struct (::).
Definition: ast.hpp:666
An if statement (if), with one or more IfClauses, and optionally an else clause.
Definition: ast.hpp:930
optional< Compound > else_clause
Definition: ast.hpp:933
vector< IfClause > clauses
Definition: ast.hpp:932
Represents an implicit cast to a different type, performed during semantic analysis.
Definition: ast.hpp:685
A local definition of an anonymous function.
Definition: ast.hpp:729
Number literals.
Definition: ast.hpp:482
The top level structure of a file of source code.
Definition: ast.hpp:957
Return from a function body.
Definition: ast.hpp:944
VarDecl * extends_lifetime
Definition: ast.hpp:947
OptionalExpr expr
Definition: ast.hpp:946
A slice literal, i.e. an array.
Definition: ast.hpp:632
String literals.
Definition: ast.hpp:521
static constexpr auto BUILTIN_TYPE_SLICE
Definition: ast.hpp:857
Represents a reference to a type.
Definition: ast.hpp:700
A declaration of a local variable (let).
Definition: ast.hpp:872
AnyExpr init
Definition: ast.hpp:876
A variable, i.e. just an identifier.
Definition: ast.hpp:534
A while loop (while).
Definition: ast.hpp:904
Compound body
Definition: ast.hpp:907
auto visit(Us... us) -> decltype(auto)
Definition: util.hpp:55
#define YUME_ASSERT(assertion, message)
Definition: util.hpp:81