aboutsummaryrefslogtreecommitdiffstats
path: root/src/ortho/llvm6/llvm-cbindings.cpp
diff options
context:
space:
mode:
authorTristan Gingold <tgingold@free.fr>2020-06-07 09:32:59 +0200
committerTristan Gingold <tgingold@free.fr>2020-06-09 18:31:58 +0200
commitaa0bd54a1de29d5e9795b7b8c249d0c7fde827a8 (patch)
tree364601610fc14c49043cbe7b5e105298e53ce843 /src/ortho/llvm6/llvm-cbindings.cpp
parentdcc6dc4eccea56104bbea43e4407ce8b82dbdab2 (diff)
downloadghdl-aa0bd54a1de29d5e9795b7b8c249d0c7fde827a8.tar.gz
ghdl-aa0bd54a1de29d5e9795b7b8c249d0c7fde827a8.tar.bz2
ghdl-aa0bd54a1de29d5e9795b7b8c249d0c7fde827a8.zip
LLVM backend with debug info (in C/C++)
Diffstat (limited to 'src/ortho/llvm6/llvm-cbindings.cpp')
-rw-r--r--src/ortho/llvm6/llvm-cbindings.cpp2264
1 files changed, 2264 insertions, 0 deletions
diff --git a/src/ortho/llvm6/llvm-cbindings.cpp b/src/ortho/llvm6/llvm-cbindings.cpp
new file mode 100644
index 000000000..6002fd05f
--- /dev/null
+++ b/src/ortho/llvm6/llvm-cbindings.cpp
@@ -0,0 +1,2264 @@
+/* LLVM binding
+ Copyright (C) 2014 Tristan Gingold
+
+ GHDL is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ GHDL is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GHDL; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+#include "llvm-c/Target.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Core.h"
+#include "llvm-c/BitWriter.h"
+#include "llvm-c/Analysis.h"
+#include "llvm-c/Transforms/Scalar.h"
+
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/DIBuilder.h"
+#include "llvm/Support/FileSystem.h"
+#include <vector>
+
+using namespace llvm;
+
+// True if the LLVM output must be displayed (set by '--dump-llvm')
+static bool FlagDumpLLVM = false;
+
+// Verify generated LLVM code.
+static bool FlagVerifyLLVM = false;
+
+static bool FlagDebugLines = true;
+
+static LLVMModuleRef TheModule;
+static LLVMTargetRef TheTarget;
+static LLVMTargetMachineRef TheTargetMachine;
+static LLVMTargetDataRef TheTargetData;
+static LLVMRelocMode TheReloc = LLVMRelocDefault;
+static LLVMCodeGenOptLevel Optimization = LLVMCodeGenLevelDefault;
+
+static LLVMBuilderRef Builder;
+static LLVMBuilderRef DeclBuilder;
+static LLVMBuilderRef ExtraBuilder;
+
+static LLVMValueRef StackSaveFun;
+static LLVMValueRef StackRestoreFun;
+static LLVMValueRef CopySignFun;
+
+static LLVMValueRef Fp0_5;
+static LLVMAttributeRef NounwindAttr;
+static LLVMAttributeRef UwtableAttr;
+
+static bool Unreach;
+
+static unsigned DebugCurrentLine;
+static std::string *DebugCurrentFilename;
+static std::string *DebugCurrentDirectory;
+static DIFile *DebugCurrentFile;
+
+static DIBuilder *DBuilder;
+
+extern "C" void
+set_optimization_level (unsigned level)
+{
+ switch(level) {
+ case 0:
+ Optimization = LLVMCodeGenLevelNone;
+ break;
+ case 1:
+ Optimization = LLVMCodeGenLevelLess;
+ break;
+ case 2:
+ Optimization = LLVMCodeGenLevelDefault;
+ break;
+ default:
+ Optimization = LLVMCodeGenLevelAggressive;
+ break;
+ }
+}
+
+extern "C" void
+set_dump_llvm (unsigned Flag)
+{
+ FlagDumpLLVM = Flag != 0;
+}
+
+extern "C" void
+set_verify_llvm (unsigned Flag)
+{
+ FlagVerifyLLVM = Flag != 0;
+}
+
+extern "C" void
+set_pic_flag (unsigned Flag)
+{
+ TheReloc = Flag ? LLVMRelocPIC : LLVMRelocStatic;
+}
+
+static void
+generateError(const char *Filename, char *Msg)
+{
+ fprintf(stderr, "error while writing to %s\n", Filename);
+ if (Msg) {
+ fprintf(stderr, "message: %s\n", Msg);
+ LLVMDisposeMessage(Msg);
+ }
+ exit(2);
+}
+
+static void
+generateCommon()
+{
+ char *Msg;
+
+ if (FlagDumpLLVM)
+ LLVMDumpModule(TheModule);
+
+ if (FlagVerifyLLVM) {
+ if (LLVMVerifyModule(TheModule, LLVMPrintMessageAction, &Msg)) {
+ LLVMDisposeMessage (Msg);
+ abort();
+ }
+ }
+
+ if (Optimization > LLVMCodeGenLevelNone) {
+ LLVMPassManagerRef PassManager;
+ PassManager = LLVMCreateFunctionPassManagerForModule (TheModule);
+
+ LLVMAddPromoteMemoryToRegisterPass (PassManager);
+ LLVMAddCFGSimplificationPass (PassManager);
+
+ for (LLVMValueRef Func = LLVMGetFirstFunction (TheModule);
+ Func != nullptr;
+ Func = LLVMGetNextFunction(Func)) {
+ LLVMRunFunctionPassManager (PassManager, Func);
+ }
+ }
+}
+extern "C" void
+generate_object(char *Filename)
+{
+ char *Msg;
+
+ generateCommon();
+
+ if (LLVMTargetMachineEmitToFile (TheTargetMachine, TheModule, Filename,
+ LLVMObjectFile, &Msg))
+ generateError(Filename, Msg);
+}
+
+extern "C" void
+generate_assembly(char *Filename)
+{
+ char *Msg;
+
+ generateCommon();
+
+ if (LLVMTargetMachineEmitToFile (TheTargetMachine, TheModule, Filename,
+ LLVMAssemblyFile, &Msg))
+ generateError(Filename, Msg);
+}
+
+extern "C" void
+generate_bitcode(const char *Filename)
+{
+ generateCommon();
+
+ if (LLVMWriteBitcodeToFile(TheModule, Filename)) {
+ generateError(Filename, nullptr);
+ }
+}
+
+extern "C" void
+generate_llvm(char *Filename)
+{
+ char *Msg;
+
+ generateCommon();
+
+ if (LLVMPrintModuleToFile(TheModule, Filename, &Msg)) {
+ generateError(Filename, Msg);
+ }
+}
+
+extern "C" void
+ortho_llvm_init(const char *Filename, unsigned FilenameLength)
+{
+ char *Msg;
+
+ LLVMInitializeNativeTarget();
+ LLVMInitializeNativeAsmPrinter();
+
+ TheModule = LLVMModuleCreateWithName ("ortho");
+
+ // Get target triple (from how llvm was configured).
+ char *Triple = LLVMGetDefaultTargetTriple();
+
+#if LLVM_VERSION_MAJOR >= 7
+ {
+ char *RawTriple = Triple;
+ Triple = LLVMNormalizeTargetTriple(Triple);
+ LLVMDisposeMessage(RawTriple);
+ }
+#endif
+ LLVMSetTarget(TheModule, Triple);
+
+ // Get target - this is a struct that corresponds to the triple.
+ if (LLVMGetTargetFromTriple(Triple, &TheTarget, &Msg) != 0) {
+ fprintf(stderr, "llvm: cannot find target %s: %s\n", Triple, Msg);
+ LLVMDisposeMessage(Msg);
+ exit (1);
+ }
+
+ // Create a target machine
+ TheTargetMachine = LLVMCreateTargetMachine
+ (TheTarget, Triple, NULL, NULL, Optimization, TheReloc,
+ LLVMCodeModelDefault);
+
+ TheTargetData = LLVMCreateTargetDataLayout(TheTargetMachine);
+ LLVMSetModuleDataLayout(TheModule, TheTargetData);
+
+ Builder = LLVMCreateBuilder();
+ DeclBuilder = LLVMCreateBuilder();
+ ExtraBuilder = LLVMCreateBuilder();
+
+ LLVMTypeRef I8Ptr = LLVMPointerType(LLVMInt8Type(), 0);
+
+ StackSaveFun = LLVMAddFunction
+ (TheModule, "llvm.stacksave", LLVMFunctionType (I8Ptr, NULL, 0, false));
+
+ LLVMTypeRef ParamTypes[2];
+
+ ParamTypes[0] = I8Ptr;
+ StackRestoreFun = LLVMAddFunction
+ (TheModule, "llvm.stackrestore",
+ LLVMFunctionType(LLVMVoidType(), ParamTypes, 1, false));
+
+ ParamTypes[0] = LLVMDoubleType();
+ ParamTypes[1] = LLVMDoubleType();
+ CopySignFun = LLVMAddFunction
+ (TheModule, "llvm.copysign.f64",
+ LLVMFunctionType(LLVMDoubleType(), ParamTypes, 2, false));
+
+ Fp0_5 = LLVMConstReal(LLVMDoubleType(), 0.5);
+
+ unsigned AttrId;
+
+ AttrId = LLVMGetEnumAttributeKindForName("nounwind", 8);
+ assert (AttrId != 0);
+ NounwindAttr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), AttrId, 0);
+
+ AttrId = LLVMGetEnumAttributeKindForName("uwtable", 7);
+ assert (AttrId != 0);
+ UwtableAttr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), AttrId, 0);
+
+ if (FlagDebugLines) {
+ DBuilder = new DIBuilder(*unwrap(TheModule));
+
+ DebugCurrentFilename = new std::string(Filename, FilenameLength);
+ SmallString<128> CurrentDir;
+ llvm::sys::fs::current_path(CurrentDir);
+ DebugCurrentDirectory = new std::string(CurrentDir.data(),
+ CurrentDir.size());
+
+ DebugCurrentFile = DBuilder->createFile(StringRef(*DebugCurrentFilename),
+ StringRef(*DebugCurrentDirectory));
+ }
+}
+
+enum OTKind : unsigned char {
+ OTKUnsigned, OTKSigned, OTKFloat,
+ OTKEnum, OTKBool,
+ OTKAccess, OTKIncompleteAccess,
+ OTKRecord, OTKIncompleteRecord,
+ OTKUnion,
+ OTKArray
+};
+
+struct OTnodeBase {
+ LLVMTypeRef Ref;
+ LLVMValueRef Dbg;
+
+ OTKind Kind;
+ bool Bounded;
+ OTnodeBase (LLVMTypeRef R, OTKind K, bool Bounded) :
+ Ref(R), Dbg(nullptr), Kind(K), Bounded(Bounded) {}
+};
+
+typedef OTnodeBase *OTnode;
+
+struct OTnodeScal : OTnodeBase {
+ // For scalar: the size
+ unsigned ScalSize;
+
+ OTnodeScal (LLVMTypeRef R, OTKind K, unsigned Sz) :
+ OTnodeBase(R, K, true), ScalSize(Sz) {}
+};
+
+struct OTnodeUnsigned : OTnodeScal {
+ OTnodeUnsigned (LLVMTypeRef R, unsigned Sz) :
+ OTnodeScal(R, OTKUnsigned, Sz) {}
+};
+
+struct OTnodeSigned : OTnodeScal {
+ OTnodeSigned (LLVMTypeRef R, unsigned Sz) :
+ OTnodeScal(R, OTKSigned, Sz) {}
+};
+
+struct OTnodeFloat : OTnodeScal {
+ OTnodeFloat (LLVMTypeRef R, unsigned Sz) :
+ OTnodeScal(R, OTKFloat, Sz) {}
+};
+
+struct OTnodeEnum : OTnodeScal {
+ OTnodeEnum (LLVMTypeRef R, unsigned Sz) :
+ OTnodeScal(R, OTKEnum, Sz) {}
+};
+
+struct OTnodeBool : OTnodeScal {
+ OTnodeBool (LLVMTypeRef R) : OTnodeScal(R, OTKBool, 1) {}
+};
+
+static LLVMTypeRef
+SizeToLLVM (unsigned Sz)
+{
+ switch (Sz) {
+ case 8:
+ return LLVMInt8Type();
+ case 32:
+ return LLVMInt32Type();
+ case 64:
+ return LLVMInt64Type();
+ default:
+ abort();
+ }
+}
+
+extern "C" OTnode
+new_unsigned_type(unsigned Sz)
+{
+ return new OTnodeUnsigned(SizeToLLVM(Sz), Sz);
+}
+
+extern "C" OTnode
+new_signed_type(unsigned Sz)
+{
+ return new OTnodeSigned(SizeToLLVM(Sz), Sz);
+}
+
+extern "C" OTnode
+new_float_type()
+{
+ return new OTnodeFloat(LLVMDoubleType(), 64);
+}
+
+struct OEnumList {
+ LLVMTypeRef Ref;
+ unsigned Pos;
+ OTnodeEnum *Etype;
+};
+
+extern "C" void
+start_enum_type (OEnumList *List, unsigned Sz)
+{
+ LLVMTypeRef T = SizeToLLVM(Sz);
+
+ *List = {T, 0, new OTnodeEnum(T, Sz)};
+}
+
+struct OCnode {
+ LLVMValueRef Ref;
+ OTnode Ctype;
+};
+
+struct OIdent {
+ const char *cstr;
+};
+
+extern "C" void
+new_enum_literal (OEnumList *List, OIdent Ident, OCnode *Res)
+{
+ *Res = {LLVMConstInt(List->Ref, List->Pos++, 0),
+ List->Etype};
+}
+
+extern "C" void
+finish_enum_type (OEnumList *List, OTnode *Res)
+{
+ *Res = List->Etype;
+}
+
+extern "C" void
+new_boolean_type(OTnode *Res,
+ OIdent False_Id, OCnode *False_E,
+ OIdent True_Id, OCnode *True_E)
+{
+ OTnodeBool *T = new OTnodeBool(LLVMInt1Type());
+ *Res = T;
+
+ *False_E = {LLVMConstInt(T->Ref, 0, 0), T};
+ *True_E = {LLVMConstInt(T->Ref, 1, 0), T};
+}
+
+extern "C" OCnode
+new_signed_literal (OTnode LType, int64_t Value)
+{
+ return {LLVMConstInt(LType->Ref, Value, 1), LType};
+}
+
+extern "C" OCnode
+new_unsigned_literal (OTnode LType, uint64_t Value)
+{
+ return {LLVMConstInt(LType->Ref, Value, 0), LType};
+}
+
+extern "C" OCnode
+new_float_literal (OTnode LType, double Value)
+{
+ return {LLVMConstReal(LType->Ref, Value), LType};
+}
+
+struct OTnodeAccBase : OTnodeBase {
+ // For accesses
+ OTnode Acc;
+
+ OTnodeAccBase (LLVMTypeRef R, OTKind Kind, OTnode Acc) :
+ OTnodeBase(R, Kind, true), Acc(Acc) {}
+};
+
+struct OTnodeAcc : OTnodeAccBase {
+ OTnodeAcc (LLVMTypeRef R, OTnode Acc) :
+ OTnodeAccBase(R, OTKAccess, Acc) {}
+};
+
+struct OTnodeIncompleteAcc : OTnodeAccBase {
+ OTnodeIncompleteAcc () :
+ OTnodeAccBase(nullptr, OTKIncompleteAccess, nullptr) {}
+};
+
+extern "C" OTnode
+new_access_type(OTnode DType)
+{
+ if (DType == nullptr) {
+ return new OTnodeIncompleteAcc();
+ } else {
+ return new OTnodeAcc(LLVMPointerType(DType->Ref, 0), DType);
+ }
+}
+
+extern "C" void
+finish_access_type(OTnodeAcc *AccType, OTnode DType)
+{
+ // Must be incomplete.
+ assert (AccType->Acc -= nullptr);
+
+ LLVMTypeRef Types[1] = { DType->Ref };
+ LLVMStructSetBody(LLVMGetElementType(AccType->Ref), Types, 1, 0);
+ AccType->Acc = DType;
+}
+
+extern "C" OCnode
+new_null_access (OTnode LType)
+{
+ return {LLVMConstNull(LType->Ref), LType};
+}
+
+enum OFKind { OF_Record, OF_Union};
+
+struct OElement {
+ // Identifier for the element
+ OIdent Ident;
+
+ // Type of the element
+ OTnode Etype;
+
+ // Next element (in the linked list)
+ OElement *Next;
+};
+
+struct OElementList {
+ OFKind Kind;
+
+ // Number of fields.
+ unsigned Count;
+
+ // For record: the access to the incomplete (but named) type.
+ OTnode RecType;
+
+ // For unions: biggest for size and alignment
+ unsigned Size;
+ unsigned Align;
+ // For unions: type with the biggest alignment.
+ LLVMTypeRef AlignType;
+
+ struct OElement *FirstElem;
+ struct OElement *LastElem;
+};
+
+extern "C" void
+start_record_type (OElementList *Elements)
+{
+ *Elements = {OF_Record,
+ 0,
+ nullptr,
+ 0, 0, nullptr,
+ nullptr,
+ nullptr};
+}
+
+static void
+addField(OElementList *Elements, OIdent Ident, OTnode Etype)
+{
+ Elements->Count++;
+
+ OElement *El = new OElement{Ident, Etype, nullptr};
+ if (Elements->FirstElem == nullptr)
+ Elements->FirstElem = El;
+ else
+ Elements->LastElem->Next = El;
+ Elements->LastElem = El;
+}
+
+struct OFnodeBase {
+ OFKind Kind;
+ OTnode FType;
+ OFnodeBase(OFKind Kind, OTnode FType) : Kind(Kind), FType(FType) {}
+};
+
+struct OFnodeRec : OFnodeBase {
+ unsigned Index;
+ OFnodeRec(OTnode Etype, unsigned Index) :
+ OFnodeBase(OF_Record, Etype), Index(Index) {}
+};
+
+struct OFnodeUnion : OFnodeBase {
+ LLVMTypeRef Utype;
+ // Pointer type - used to do conversion between the union and the field.
+ LLVMTypeRef PtrType;
+ OFnodeUnion(OTnode Etype, LLVMTypeRef PtrType) :
+ OFnodeBase(OF_Union, Etype), Utype(Etype->Ref), PtrType(PtrType) {}
+};
+
+extern "C" void
+new_record_field(OElementList *Elements,
+ OFnodeRec **El, OIdent Ident, OTnode Etype)
+{
+ *El = new OFnodeRec(Etype, Elements->Count);
+ addField(Elements, Ident, Etype);
+}
+
+static void
+freeElements(OElementList *Els)
+{
+ OElement *El, *NEl;
+
+ for (El = Els->FirstElem; El != nullptr; El = NEl) {
+ NEl = El->Next;
+ delete El;
+ }
+ Els->FirstElem = nullptr;
+ Els->LastElem = nullptr;
+}
+
+struct OTnodeRecBase : OTnodeBase {
+ OTnodeRecBase (LLVMTypeRef R, OTKind Kind, bool Bounded) :
+ OTnodeBase(R, Kind, Bounded) {}
+};
+
+struct OTnodeRec : OTnodeRecBase {
+ OTnodeRec (LLVMTypeRef R, bool Bounded) :
+ OTnodeRecBase(R, OTKRecord, Bounded) {}
+};
+
+struct OTnodeIncompleteRec : OTnodeRecBase {
+ OTnodeIncompleteRec () :
+ OTnodeRecBase(nullptr, OTKIncompleteRecord, false) {}
+};
+
+extern "C" void
+finish_record_type(OElementList *Els, OTnode *Res)
+{
+ LLVMTypeRef *Types = new LLVMTypeRef[Els->Count];
+
+ OElement *El;
+ int i;
+ bool Bounded = true;
+ for (i = 0, El = Els->FirstElem; El != nullptr; El = El->Next, i++) {
+ Bounded &= El->Etype->Bounded;
+ Types[i] = El->Etype->Ref;
+ }
+
+ if (Els->RecType != nullptr) {
+ // Completion
+ LLVMStructSetBody (Els->RecType->Ref, Types, Els->Count, 0);
+ Els->RecType->Bounded = Bounded;
+ *Res = Els->RecType;
+ } else {
+ *Res = new OTnodeRec(LLVMStructType(Types, Els->Count, 0), Bounded);
+ }
+ freeElements(Els);
+}
+
+extern "C" void
+new_uncomplete_record_type(OTnode *Res)
+{
+ *Res = new OTnodeIncompleteRec();
+}
+
+extern "C" void
+start_uncomplete_record_type(OTnodeRec *Res, OElementList *Els)
+{
+ // Must be incomplete.
+ assert (Res->Ref == nullptr);
+
+ *Els = {OF_Record,
+ 0,
+ Res,
+ 0, 0, nullptr,
+ nullptr,
+ nullptr};
+}
+
+extern "C" void
+start_union_type(OElementList *Els)
+{
+ *Els = {OF_Union,
+ 0,
+ nullptr,
+ 0, 0, nullptr,
+ nullptr,
+ nullptr};
+}
+
+extern "C" void
+new_union_field(OElementList *Els, OFnodeUnion **El,
+ OIdent Ident, OTnode Etype)
+{
+ unsigned Size = LLVMABISizeOfType(TheTargetData, Etype->Ref);
+ unsigned Align = LLVMABIAlignmentOfType(TheTargetData, Etype->Ref);
+
+ *El = new OFnodeUnion(Etype, LLVMPointerType(Etype->Ref, 0));
+
+ if (Size > Els->Size)
+ Els->Size = Size;
+ if (Els->AlignType == nullptr || Align > Els->Align) {
+ Els->Align = Align;
+ Els->AlignType = Etype->Ref;
+ }
+ addField(Els, Ident, Etype);
+}
+
+struct OTnodeUnion : OTnodeBase {
+ // For unions
+ unsigned Size;
+ LLVMTypeRef MainField;
+
+ OTnodeUnion(LLVMTypeRef R, unsigned Sz, LLVMTypeRef Main) :
+ OTnodeBase(R, OTKUnion, true), Size(Sz), MainField(Main) {}
+};
+
+
+extern "C" void
+finish_union_type(OElementList *Els, OTnode *Res)
+{
+ unsigned Count;
+ LLVMTypeRef Types[2];
+
+ if (Els->AlignType == nullptr) {
+ // An empty union
+ Count = 0;
+ } else {
+ unsigned Pad;
+
+ Types[0] = Els->AlignType;
+ Pad = Els->Size - LLVMABISizeOfType(TheTargetData, Els->AlignType);
+ if (Pad != 0) {
+ Types[1] = LLVMArrayType(LLVMInt8Type(), Pad);
+ Count = 2;
+ } else {
+ Count = 1;
+ }
+ }
+
+ *Res = new OTnodeUnion(LLVMStructType(Types, Count, 0),
+ Els->Size, Els->AlignType);
+ freeElements(Els);
+}
+
+struct OTnodeArr : OTnodeBase {
+ // For arrays: type of the element
+ OTnode ElType;
+
+ OTnodeArr(LLVMTypeRef R, bool Complete, OTnode E) :
+ OTnodeBase(R, OTKArray, Complete), ElType(E) {}
+};
+
+extern "C" OTnode
+new_array_type(OTnode ElType, OTnode IndexType)
+{
+ return new OTnodeArr(LLVMArrayType(ElType->Ref, 0), false, ElType);
+}
+
+extern "C" OTnode
+new_constrained_array_type(OTnodeArr *ArrType, OCnode *Length)
+{
+ unsigned Len = LLVMConstIntGetZExtValue(Length->Ref);
+
+ return new OTnodeArr(LLVMArrayType(ArrType->ElType->Ref, Len),
+ ArrType->ElType->Bounded,
+ ArrType->ElType);
+}
+
+extern "C" void
+new_type_decl(OIdent Ident, OTnode Atype)
+{
+ switch(Atype->Kind) {
+ case OTKIncompleteAccess:
+ Atype->Ref = LLVMPointerType
+ (LLVMStructCreateNamed(LLVMGetGlobalContext(), Ident.cstr), 0);
+ break;
+ case OTKIncompleteRecord:
+ Atype->Ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), Ident.cstr);
+ break;
+ default:
+ break;
+ }
+}
+
+struct ORecordAggrList {
+ unsigned Len;
+ LLVMValueRef *Els;
+ OTnode Atype;
+};
+
+extern "C" void
+start_record_aggr(ORecordAggrList *List, OTnode Atype)
+{
+ unsigned Count = LLVMCountStructElementTypes(Atype->Ref);
+ *List = {0, new LLVMValueRef[Count], Atype};
+}
+
+extern "C" void
+new_record_aggr_el(ORecordAggrList *List, OCnode *Val)
+{
+ List->Els[List->Len++] = Val->Ref;
+}
+
+extern "C" void
+finish_record_aggr(ORecordAggrList *List, OCnode *Res)
+{
+ *Res = {LLVMConstStruct(List->Els, List->Len, 0), List->Atype};
+ delete List->Els;
+}
+
+struct OArrayAggrList {
+ unsigned Len;
+ LLVMValueRef *Els;
+ LLVMTypeRef ElType;
+ OTnode Atype;
+};
+
+extern "C" void
+start_array_aggr(OArrayAggrList *List, OTnodeArr *Atype, unsigned len)
+{
+ *List = {0, new LLVMValueRef[len], Atype->ElType->Ref, Atype};
+}
+
+extern "C" void
+new_array_aggr_el(OArrayAggrList *List, OCnode *Value)
+{
+ List->Els[List->Len++] = Value->Ref;
+}
+
+extern "C" void
+finish_array_aggr(OArrayAggrList *List, OCnode *Res)
+{
+ *Res = {LLVMConstArray(List->ElType, List->Els, List->Len), List->Atype};
+ delete List->Els;
+}
+
+extern "C" OCnode
+new_union_aggr(OTnodeUnion *Atype, OFnodeUnion *Field, OCnode *Value)
+{
+ unsigned Size = LLVMABISizeOfType(TheTargetData, Field->Utype);
+ LLVMValueRef Vals[2];
+ unsigned Count;
+
+ Vals[0] = Value->Ref;
+ if (Size < Atype->Size) {
+ // Add padding.
+ Vals[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), Atype->Size - Size));
+ Count = 2;
+ } else {
+ Count = 1;
+ }
+
+ return {LLVMConstStruct(Vals, Count, false), Atype};
+}
+
+extern "C" OCnode
+new_default_value(OTnode Ltype)
+{
+ return {LLVMConstNull(Ltype->Ref), Ltype};
+}
+
+static OCnode
+constToConst(OTnode Rtype, uint64_t Val)
+{
+ LLVMValueRef Ref;
+
+ switch (Rtype->Kind) {
+ case OTKUnsigned:
+ case OTKSigned:
+ Ref = LLVMConstInt(Rtype->Ref, Val, 0);
+ break;
+ case OTKAccess:
+ // It is possible to use an access type for offsetof.
+ Ref = LLVMConstInt(LLVMInt64Type(), Val, 0);
+ Ref = LLVMConstIntToPtr(Ref, Rtype->Ref);
+ break;
+ default:
+ abort();
+ }
+ return {Ref, Rtype};
+}
+
+extern "C" OCnode
+new_sizeof(OTnode Atype, OTnode Rtype)
+{
+ return constToConst(Rtype, LLVMABISizeOfType(TheTargetData, Atype->Ref));
+}
+
+extern "C" OCnode
+new_alignof(OTnode Atype, OTnode Rtype)
+{
+ return constToConst
+ (Rtype, LLVMABIAlignmentOfType(TheTargetData, Atype->Ref));
+}
+
+extern "C" OCnode
+new_offsetof(OTnode Atype, OFnodeRec *Field, OTnode Rtype)
+{
+ return constToConst
+ (Rtype, LLVMOffsetOfElement(TheTargetData, Atype->Ref, Field->Index));
+}
+
+struct OEnode {
+ LLVMValueRef Ref;
+ OTnode Etype;
+};
+
+extern "C" OEnode
+new_lit(OCnode *Lit)
+{
+ return {Lit->Ref, Lit->Ctype};
+}
+
+enum ODKind : unsigned char {
+ ODKConst,
+ ODKVar,
+ ODKLocal,
+ ODKInterface,
+ ODKType,
+ ODKSubprg
+};
+
+struct ODnodeBase {
+ LLVMValueRef Ref;
+ OTnode Dtype;
+ virtual ODKind getKind() const = 0;
+ ODnodeBase(LLVMValueRef R, OTnode T) : Ref(R), Dtype(T) {}
+ virtual ~ODnodeBase() {}
+};
+
+typedef ODnodeBase *ODnode;
+
+struct ODnodeVar : ODnodeBase {
+ ODKind getKind() const override { return ODKVar; }
+ ODnodeVar(LLVMValueRef R, OTnode T) : ODnodeBase(R, T) {}
+};
+
+struct ODnodeLocalVar : ODnodeBase {
+ ODKind getKind() const override { return ODKLocal; }
+ ODnodeLocalVar(LLVMValueRef R, OTnode T) : ODnodeBase(R, T) {}
+};
+
+enum OStorage {
+ O_Storage_External,
+ O_Storage_Public,
+ O_Storage_Private,
+ O_Storage_Local
+};
+
+extern "C" void
+new_var_decl(ODnode *Res, OIdent Ident, OStorage Storage, OTnode Atype)
+{
+ LLVMValueRef Decl;
+
+ if (Storage == O_Storage_Local) {
+ if (Unreach)
+ Decl = nullptr;
+ else
+ Decl = LLVMBuildAlloca (DeclBuilder, Atype->Ref, Ident.cstr);
+ *Res = new ODnodeLocalVar(Decl, Atype);
+ } else {
+ if (Storage == O_Storage_External) {
+ Decl = LLVMGetNamedGlobal(TheModule, Ident.cstr);
+ } else {
+ Decl = nullptr;
+ }
+ if (Decl == nullptr)
+ Decl = LLVMAddGlobal(TheModule, Atype->Ref, Ident.cstr);
+
+ *Res = new ODnodeVar(Decl, Atype);
+ if (Storage == O_Storage_Private)
+ LLVMSetLinkage(Decl, LLVMInternalLinkage);
+
+ switch(Storage) {
+ case O_Storage_Public:
+ case O_Storage_Private:
+ LLVMSetInitializer(Decl, LLVMConstNull(Atype->Ref));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+struct ODnodeConst : ODnodeBase {
+ OStorage Storage;
+ OIdent Ident;
+ ODKind getKind() const override { return ODKConst; }
+ ODnodeConst(LLVMValueRef R, OTnode T, OStorage S, OIdent I) :
+ ODnodeBase(R, T), Storage(S), Ident(I) {}
+};
+
+static void
+setConstAttributes(LLVMValueRef Ref, OStorage Storage)
+{
+ LLVMSetGlobalConstant(Ref, true);
+ if (Storage == O_Storage_Private)
+ LLVMSetLinkage(Ref, LLVMInternalLinkage);
+}
+
+extern "C" void
+new_const_decl(ODnode *Res, OIdent Ident, OStorage Storage, OTnode Atype)
+{
+ LLVMValueRef Decl;
+
+ if (Storage == O_Storage_External) {
+ // It is possible to re-declare an external const.
+ Decl = LLVMGetNamedGlobal(TheModule, Ident.cstr);
+ if (Decl == nullptr)
+ Decl = LLVMAddGlobal(TheModule, Atype->Ref, Ident.cstr);
+ setConstAttributes(Decl, Storage);
+ } else {
+ // If the type of the constant is not yet bounded, delay the creation
+ // of the constant until its initialization.
+ if (Atype->Bounded) {
+ Decl = LLVMAddGlobal(TheModule, Atype->Ref, Ident.cstr);
+ setConstAttributes(Decl, Storage);
+ } else {
+ Decl = nullptr;
+ }
+ }
+
+ *Res = new ODnodeConst(Decl, Atype, Storage, Ident);
+}
+
+extern "C" void
+start_init_value(ODnodeConst **Decl)
+{
+}
+
+extern "C" void
+finish_init_value(ODnodeConst **Decl, OCnode *Val)
+{
+ LLVMValueRef Ref = (*Decl)->Ref;
+
+ if (Ref == nullptr) {
+ Ref = LLVMAddGlobal(TheModule, LLVMTypeOf(Val->Ref), (*Decl)->Ident.cstr);
+ setConstAttributes(Ref, (*Decl)->Storage);
+ (*Decl)->Ref = Ref;
+ }
+
+ LLVMSetInitializer(Ref, Val->Ref);
+}
+
+struct ODnodeInter : ODnodeBase {
+ ODKind getKind() const override { return ODKInterface; }
+ ODnodeInter(LLVMValueRef R, OTnode T) : ODnodeBase(R, T) {}
+};
+
+struct OInter {
+ ODnodeInter *Decl;
+ OIdent Ident;
+ OInter *Next;
+};
+
+struct OInterList {
+ OIdent Ident;
+ OStorage Storage;
+ OTnode Rtype;
+
+ // Number of interfaces.
+ unsigned Count;
+ OInter *FirstInter;
+ OInter *LastInter;
+};
+
+extern "C" void
+start_function_decl(OInterList *Inters, OIdent Ident, OStorage Storage,
+ OTnode Rtype)
+{
+ *Inters = { Ident, Storage, Rtype, 0, nullptr, nullptr };
+}
+
+extern "C" void
+start_procedure_decl(OInterList *Inters, OIdent Ident, OStorage Storage)
+{
+ *Inters = { Ident, Storage, nullptr, 0, nullptr, nullptr };
+}
+
+extern "C" void
+new_interface_decl(OInterList *Inters,
+ ODnode *Res, OIdent Ident, OTnode Itype)
+{
+ ODnodeInter *Decl = new ODnodeInter(nullptr, Itype);
+ OInter *Inter = new OInter{Decl, Ident, nullptr};
+
+ *Res = Decl;
+ Inters->Count++;
+ if (Inters->FirstInter == nullptr)
+ Inters->FirstInter = Inter;
+ else
+ Inters->LastInter->Next = Inter;
+ Inters->LastInter = Inter;
+}
+
+struct ODnodeSubprg : ODnodeBase {
+ // Number of interfaces.
+ unsigned Count;
+ ODKind getKind() const override { return ODKSubprg; }
+ ODnodeSubprg(LLVMValueRef R, OTnode T, unsigned Count) :
+ ODnodeBase(R, T), Count(Count) {}
+};
+
+extern "C" void
+finish_subprogram_decl(OInterList *Inters, ODnode *Res)
+{
+ LLVMTypeRef *Types = new LLVMTypeRef[Inters->Count];
+
+ // Build array of interface types.
+ int i = 0;
+ for (OInter *Inter = Inters->FirstInter; Inter; Inter = Inter->Next, i++)
+ Types[i] = Inter->Decl->Dtype->Ref;
+
+ LLVMTypeRef Rtype;
+ if (Inters->Rtype == nullptr)
+ Rtype = LLVMVoidType();
+ else
+ Rtype = Inters->Rtype->Ref;
+
+ LLVMTypeRef Ftype = LLVMFunctionType(Rtype, Types, Inters->Count, 0);
+
+ LLVMValueRef Decl;
+ if (Inters->Storage == O_Storage_External)
+ Decl = LLVMGetNamedFunction(TheModule, Inters->Ident.cstr);
+ else
+ Decl = nullptr;
+ if (Decl == nullptr) {
+ Decl = LLVMAddFunction(TheModule, Inters->Ident.cstr, Ftype);
+ LLVMAddAttributeAtIndex(Decl, LLVMAttributeFunctionIndex, NounwindAttr);
+ LLVMAddAttributeAtIndex(Decl, LLVMAttributeFunctionIndex, UwtableAttr);
+ LLVMSetFunctionCallConv(Decl, LLVMCCallConv);
+ }
+
+ *Res = new ODnodeSubprg(Decl, Inters->Rtype, Inters->Count);
+
+ // Translate interfaces
+ i = 0;
+ for (OInter *Inter = Inters->FirstInter, *Next; Inter; Inter = Next, i++) {
+ Inter->Decl->Ref = LLVMGetParam(Decl, i);
+ LLVMSetValueName(Inter->Decl->Ref, Inter->Ident.cstr);
+ Next = Inter->Next;
+ delete Inter;
+ }
+}
+
+// Data for a declare block.
+struct DeclareBlock {
+ // First basic block of the declare.
+ LLVMBasicBlockRef StmtBB;
+
+ // To handle allocb: stack pointer at the entry of the block, that needs
+ // to be restored when leaving the block (either by falling through or
+ // via exit/next). Set only of New_Alloca is used.
+ LLVMValueRef StackValue;
+
+ // Previous value block.
+ DeclareBlock *Prev;
+};
+
+static DeclareBlock *CurrentDeclareBlock;
+static DeclareBlock *OldDeclareBlock;
+
+static LLVMValueRef CurrentFunc;
+static ODnodeSubprg *CurrentFuncDecl;
+
+static void
+CreateDeclareBlock()
+{
+ DeclareBlock *Res;
+
+ // Allocate a declare block
+ if (OldDeclareBlock != nullptr) {
+ Res = OldDeclareBlock;
+ OldDeclareBlock = Res->Prev;
+ } else {
+ Res = new DeclareBlock;
+ }
+ *Res = { nullptr, nullptr, CurrentDeclareBlock };
+ CurrentDeclareBlock = Res;
+
+ if (!Unreach) {
+ Res->StmtBB = LLVMAppendBasicBlock(CurrentFunc, "");
+ }
+}
+
+static void
+DestroyDeclareBlock()
+{
+ DeclareBlock *Blk = CurrentDeclareBlock;
+
+ CurrentDeclareBlock = Blk->Prev;
+
+ Blk->Prev = OldDeclareBlock;
+ OldDeclareBlock = Blk;
+}
+
+extern "C" void
+start_subprogram_body(ODnodeSubprg *Func)
+{
+ LLVMBasicBlockRef DeclBB;
+
+ // Nested subprograms are not supported.
+ assert (CurrentFunc == nullptr);
+
+ CurrentFunc = Func->Ref;
+ CurrentFuncDecl = Func;
+
+ assert(!Unreach);
+
+ DeclBB = LLVMAppendBasicBlock(CurrentFunc, "");
+ LLVMPositionBuilderAtEnd(DeclBuilder, DeclBB);
+
+ CreateDeclareBlock();
+ LLVMPositionBuilderAtEnd(Builder, CurrentDeclareBlock->StmtBB);
+}
+
+extern "C" void
+finish_subprogram_body()
+{
+ // Add a jump from the declare basic block to the first statement BB.
+ LLVMBuildBr(DeclBuilder, CurrentDeclareBlock->StmtBB);
+
+ // Terminate the statement BB
+ if (!Unreach) {
+ if (CurrentFuncDecl->Dtype == nullptr)
+ LLVMBuildRetVoid (Builder);
+ else
+ LLVMBuildUnreachable (Builder);
+ }
+
+ DestroyDeclareBlock();
+
+ CurrentFunc = nullptr;
+ Unreach = false;
+}
+
+extern "C" void
+start_declare_stmt ()
+{
+ CreateDeclareBlock();
+
+ if (Unreach)
+ return;
+
+ // Add a jump to the new BB.
+ LLVMBuildBr(Builder, CurrentDeclareBlock->StmtBB);
+
+ LLVMPositionBuilderAtEnd(Builder, CurrentDeclareBlock->StmtBB);
+}
+
+extern "C" void
+finish_declare_stmt ()
+{
+ if (!Unreach) {
+ LLVMBasicBlockRef Bb;
+
+ // Create a basic block for the statements after the dclare
+ Bb = LLVMAppendBasicBlock(CurrentFunc, "");
+
+ if (CurrentDeclareBlock->StackValue != nullptr) {
+ // Restore stack pointer
+ LLVMBuildCall(Builder, StackRestoreFun,
+ &CurrentDeclareBlock->StackValue, 1, "");
+ }
+ // Execution will continue on the next statement
+ LLVMBuildBr(Builder, Bb);
+
+ LLVMPositionBuilderAtEnd(Builder, Bb);
+ }
+
+ // Do not reset Unreach.
+ DestroyDeclareBlock();
+}
+
+struct OSNode {
+ // BB at the entry of the loop. Will branch to it on next statement and
+ // at the end of the loop.
+ LLVMBasicBlockRef BBEntry;
+ // BB after the loop. Exit statement branches to it.
+ LLVMBasicBlockRef BBExit;
+};
+
+extern "C" void
+start_loop_stmt (OSNode *Label)
+{
+ if (Unreach) {
+ *Label = { nullptr, nullptr };
+ return;
+ }
+
+ *Label = { LLVMAppendBasicBlock(CurrentFunc, ""), nullptr };
+#if 1
+ Label->BBExit = LLVMAppendBasicBlock(CurrentFunc, "");
+#endif
+ LLVMBuildBr(Builder, Label->BBEntry);
+ LLVMPositionBuilderAtEnd(Builder, Label->BBEntry);
+}
+
+extern "C" void
+finish_loop_stmt (OSNode *Label)
+{
+ if (!Unreach)
+ LLVMBuildBr(Builder, Label->BBEntry);
+
+ if (Label->BBExit != nullptr) {
+ // Continue only if the exit was reachable.
+ LLVMPositionBuilderAtEnd(Builder, Label->BBExit);
+ Unreach = false;
+ } else {
+ Unreach = true;
+ }
+}
+
+extern "C" void
+new_exit_stmt (OSNode *Label)
+{
+ if (Unreach)
+ return;
+
+#if 0
+ // Currently LABEL is an input (so cannot be modified)
+ if (Label->BBExit == nullptr) {
+ // We know the end of the loop is reachable
+ Label->BBExit = LLVMAppendBasicBlock(CurrentFunc, "");
+ }
+#endif
+
+ LLVMBuildBr(Builder, Label->BBExit);
+ Unreach = true;
+}
+
+extern "C" void
+new_next_stmt (OSNode *Label)
+{
+ if (Unreach)
+ return;
+
+ LLVMBuildBr(Builder, Label->BBEntry);
+ Unreach = true;
+}
+
+struct OIFBlock {
+ LLVMBasicBlockRef Bb;
+};
+
+extern "C" void
+start_if_stmt (OIFBlock *Blk, OEnode Cond)
+{
+ if (Unreach) {
+ *Blk = { nullptr};
+ return;
+ }
+
+ LLVMBasicBlockRef BBThen;
+
+ // Create BB for Then and Else.
+ BBThen = LLVMAppendBasicBlock(CurrentFunc, "");
+ *Blk = { LLVMAppendBasicBlock(CurrentFunc, "") };
+
+ LLVMBuildCondBr(Builder, Cond.Ref, BBThen, Blk->Bb);
+ LLVMPositionBuilderAtEnd(Builder, BBThen);
+}
+
+extern "C" void
+new_else_stmt (OIFBlock *Blk)
+{
+ LLVMBasicBlockRef BBNext;
+
+ if (!Unreach) {
+ // Create a BB for after the If statement
+ BBNext = LLVMAppendBasicBlock(CurrentFunc, "");
+ // And jump to it.
+ LLVMBuildBr(Builder, BBNext);
+ } else {
+ if (Blk->Bb == nullptr) {
+ // The IF statement was unreachable, so is the Else part.
+ return;
+ }
+ // Do not yet create the BB for after the If statement, as we don't
+ // know if it is reachable.
+ BBNext = nullptr;
+ }
+
+ // Use the BB for the Else part.
+ LLVMPositionBuilderAtEnd(Builder, Blk->Bb);
+
+ Blk->Bb = BBNext;
+ // The Else part is reachable.
+ Unreach = false;
+}
+
+extern "C" void
+finish_if_stmt (OIFBlock *Blk)
+{
+ LLVMBasicBlockRef BBNext;
+
+ if (!Unreach) {
+ if (Blk->Bb == nullptr)
+ BBNext = LLVMAppendBasicBlock(CurrentFunc, "");
+ else
+ BBNext = Blk->Bb;
+ LLVMBuildBr(Builder, BBNext);
+ LLVMPositionBuilderAtEnd(Builder, BBNext);
+ } else {
+ // The branch doesn't continue.
+ if (Blk->Bb != nullptr) {
+ // There is at least one fall-through (either from the Then or from
+ // the Else.
+ Unreach = false;
+ LLVMPositionBuilderAtEnd(Builder, Blk->Bb);
+ }
+ }
+}
+
+struct OChoice {
+ LLVMValueRef Low, High;
+ LLVMBasicBlockRef BB;
+};
+
+struct OCaseBlock {
+ // BB before the case.
+ LLVMBasicBlockRef BBPrev;
+
+ // Select expression
+ LLVMValueRef Value;
+ OTnode Vtype;
+
+ // BB after the case statement
+ LLVMBasicBlockRef BBNext;
+
+ // BB for others
+ LLVMBasicBlockRef BBOthers;
+
+ // BB for the current choice
+ LLVMBasicBlockRef BBChoice;
+
+ std::vector<OChoice> *Choices;
+};
+
+extern "C" void
+start_case_stmt (OCaseBlock *Blk, OEnode Value)
+{
+ LLVMBasicBlockRef BB;
+ std::vector<OChoice> *Choices;
+
+ if (Unreach) {
+ // The case statement is unreachable, discard it completly.
+ BB = nullptr;
+ Choices = nullptr;
+ } else {
+ BB = LLVMGetInsertBlock(Builder);
+ Choices = new std::vector<OChoice>;
+ }
+
+ *Blk = { BB,
+ Value.Ref,
+ Value.Etype,
+ nullptr,
+ nullptr,
+ nullptr,
+ Choices };
+}
+
+// Close previous branch
+static void
+finishBranch (OCaseBlock *Blk)
+{
+ if (Unreach) {
+ // No need to close it as this point is not reachable.
+ return;
+ }
+
+ if (Blk->BBNext == nullptr) {
+ // Create the BB for after the case statement.
+ // It also means the end is reachable.
+ Blk->BBNext = LLVMAppendBasicBlock(CurrentFunc, "");
+ }
+ LLVMBuildBr(Builder, Blk->BBNext);
+}
+
+extern "C" void
+start_choice (OCaseBlock *Blk)
+{
+ if (Blk->BBPrev == nullptr) {
+ // The wholse case statement was unreachable
+ assert(Unreach);
+ return;
+ }
+
+ if (Blk->BBChoice != nullptr) {
+ // Close previous branch
+ finishBranch(Blk);
+ }
+
+ // This new choice is reachable from the start of the case statement.
+ Unreach = false;
+
+ // Create a new BB.
+ Blk->BBChoice = LLVMAppendBasicBlock(CurrentFunc, "");
+ LLVMPositionBuilderAtEnd(Builder, Blk->BBChoice);
+}
+
+// Add a choice that will branch to Blk->BBChoice.
+static void
+newChoice(OCaseBlock *Blk, LLVMValueRef Low, LLVMValueRef High)
+{
+ if (Unreach)
+ return;
+
+ Blk->Choices->push_back({Low, High, Blk->BBChoice});
+}
+
+extern "C" void
+new_expr_choice (OCaseBlock *Blk, OCnode *Expr)
+{
+ newChoice(Blk, Expr->Ref, nullptr);
+}
+
+extern "C" void
+new_range_choice (OCaseBlock *Blk, OCnode *Low, OCnode *High)
+{
+ newChoice(Blk, Low->Ref, High->Ref);
+}
+
+extern "C" void
+new_default_choice (OCaseBlock *Blk)
+{
+ if (Unreach)
+ return;
+
+ Blk->BBOthers = Blk->BBChoice;
+}
+
+extern "C" void
+finish_choice (OCaseBlock *Blk)
+{
+}
+
+extern "C" void
+finish_case_stmt (OCaseBlock *Blk)
+{
+ LLVMIntPredicate GE, LE;
+
+ if (Blk->BBPrev == nullptr) {
+ // The whole case statement is not reachable.
+ return;
+ }
+
+ if (Blk->BBChoice != nullptr) {
+ // Close previous branch
+ finishBranch(Blk);
+ }
+
+ // Strategy: use a switch instruction for simple choices, put range choices
+ // in the default branch, using if statements.
+ // TODO: could improve the handling of ranges (dichotomy, decision tree...)
+ switch (Blk->Vtype->Kind) {
+ case OTKUnsigned:
+ case OTKEnum:
+ case OTKBool:
+ GE = LLVMIntUGE;
+ LE = LLVMIntULE;
+ break;
+ case OTKSigned:
+ GE = LLVMIntSGE;
+ LE = LLVMIntSLE;
+ break;
+ default:
+ llvm_unreachable();
+ }
+
+ // BB for the default case.
+ LLVMBasicBlockRef BBDefault = LLVMAppendBasicBlock(CurrentFunc, "");
+ LLVMPositionBuilderAtEnd(Builder, BBDefault);
+
+ // Put range choices in the default case.
+ unsigned int Count = 0;
+ LLVMBasicBlockRef BBLast = BBDefault;
+ for(auto &c: *Blk->Choices) {
+ if (c.High != nullptr) {
+ BBLast = LLVMAppendBasicBlock(CurrentFunc, "");
+ LLVMBuildCondBr(Builder,
+ LLVMBuildAnd(Builder,
+ LLVMBuildICmp(Builder, GE,
+ Blk->Value, c.Low, ""),
+ LLVMBuildICmp(Builder, LE,
+ Blk->Value, c.High, ""),
+ ""),
+ c.BB, BBLast);
+ LLVMPositionBuilderAtEnd(Builder, BBLast);
+ } else {
+ Count++;
+ }
+ }
+
+ // Insert the switch
+ LLVMPositionBuilderAtEnd(Builder, Blk->BBPrev);
+ LLVMValueRef Sw = LLVMBuildSwitch(Builder, Blk->Value, BBDefault, Count);
+ for(auto &c: *Blk->Choices) {
+ if (c.High == nullptr) {
+ LLVMAddCase(Sw, c.Low, c.BB);
+ }
+ }
+
+ // Insert the others (if there is one).
+ LLVMPositionBuilderAtEnd(Builder, BBLast);
+ if (Blk->BBOthers != nullptr)
+ LLVMBuildBr(Builder, Blk->BBOthers);
+ else
+ LLVMBuildUnreachable(Builder);
+
+ // Next BB.
+ if (Blk->BBNext != nullptr) {
+ Unreach = false;
+ LLVMPositionBuilderAtEnd(Builder, Blk->BBNext);
+ } else {
+ // No branch falls through
+ Unreach = true;
+ }
+ delete Blk->Choices;
+}
+
+struct OAssocList {
+ ODnodeSubprg *Subprg;
+ unsigned Idx;
+ LLVMValueRef *Vals;
+};
+
+extern "C" void
+start_association (OAssocList *Assocs, ODnodeSubprg *Subprg)
+{
+ *Assocs = { Subprg, 0, new LLVMValueRef[Subprg->Count] };
+}
+
+extern "C" void
+new_association (OAssocList *Assocs, OEnode Val)
+{
+ Assocs->Vals[Assocs->Idx++] = Val.Ref;
+}
+
+extern "C" OEnode
+new_function_call (OAssocList *Assocs)
+{
+ LLVMValueRef Res;
+
+ if (!Unreach) {
+ Res = LLVMBuildCall(Builder, Assocs->Subprg->Ref,
+ Assocs->Vals, Assocs->Subprg->Count, "");
+ } else {
+ Res = nullptr;
+ }
+ delete Assocs->Vals;
+ return { Res, Assocs->Subprg->Dtype };
+}
+
+extern "C" void
+new_procedure_call (OAssocList *Assocs)
+{
+ if (!Unreach) {
+ LLVMBuildCall(Builder, Assocs->Subprg->Ref,
+ Assocs->Vals, Assocs->Subprg->Count, "");
+ }
+ delete Assocs->Vals;
+}
+
+extern "C" void
+new_func_return_stmt (OEnode Value)
+{
+ if (Unreach)
+ return;
+ LLVMBuildRet(Builder, Value.Ref);
+ Unreach = true;
+}
+
+extern "C" void
+new_proc_return_stmt ()
+{
+ if (Unreach)
+ return;
+ LLVMBuildRetVoid(Builder);
+ Unreach = true;
+}
+
+enum ONOpKind {
+ /* Not an operation; invalid. */
+ ON_Nil,
+
+ /* Dyadic operations. */
+ ON_Add_Ov,
+ ON_Sub_Ov,
+ ON_Mul_Ov,
+ ON_Div_Ov,
+ ON_Rem_Ov,
+ ON_Mod_Ov,
+
+ /* Binary operations. */
+ ON_And,
+ ON_Or,
+ ON_Xor,
+
+ /* Monadic operations. */
+ ON_Not,
+ ON_Neg_Ov,
+ ON_Abs_Ov,
+
+ /* Comparaisons */
+ ON_Eq,
+ ON_Neq,
+ ON_Le,
+ ON_Lt,
+ ON_Ge,
+ ON_Gt,
+
+ ON_LAST
+};
+
+struct ComparePred {
+ LLVMIntPredicate SignedPred;
+ LLVMIntPredicate UnsignedPred;
+ LLVMRealPredicate RealPred;
+};
+
+static const ComparePred CompareTable[] = {
+ {LLVMIntEQ, LLVMIntEQ, LLVMRealOEQ }, // Eq
+ {LLVMIntNE, LLVMIntNE, LLVMRealONE }, // Ne
+ {LLVMIntSLE, LLVMIntULE, LLVMRealOLE }, // Le
+ {LLVMIntSLT, LLVMIntULT, LLVMRealOLT }, // Lt
+ {LLVMIntSGE, LLVMIntUGE, LLVMRealOGE }, // Ge
+ {LLVMIntSGT, LLVMIntUGT, LLVMRealOGT } // Gt
+};
+
+extern "C" OEnode
+new_compare_op (ONOpKind Kind, OEnode Left, OEnode Right, OTnode Rtype)
+{
+ LLVMValueRef Res;
+
+ if (Unreach)
+ return {nullptr, Rtype};
+
+ // Cannot apply C convention to ON_Op_Kind, so we need to truncate it
+ // (as it is represented by a byte from Ada and by int from C)
+ Kind = static_cast<ONOpKind>(Kind & 0xff);
+
+ switch(Left.Etype->Kind) {
+ case OTKUnsigned:
+ case OTKEnum:
+ case OTKBool:
+ case OTKAccess:
+ case OTKIncompleteAccess:
+ Res = LLVMBuildICmp(Builder, CompareTable[Kind - ON_Eq].UnsignedPred,
+ Left.Ref, Right.Ref, "");
+ break;
+ case OTKSigned:
+ Res = LLVMBuildICmp(Builder, CompareTable[Kind - ON_Eq].SignedPred,
+ Left.Ref, Right.Ref, "");
+ break;
+ case OTKFloat:
+ Res = LLVMBuildFCmp(Builder, CompareTable[Kind - ON_Eq].RealPred,
+ Left.Ref, Right.Ref, "");
+ break;
+ default:
+ abort();
+ }
+ return {Res, Rtype};
+}
+
+extern "C" OEnode
+new_monadic_op (ONOpKind Kind, OEnode Operand)
+{
+ LLVMValueRef Res;
+
+ if (Unreach)
+ return { nullptr, Operand.Etype};
+
+ // Cannot apply C convention to ON_Op_Kind, so we need to truncate it
+ // (as it is represented by a byte from Ada and by int from C)
+ Kind = static_cast<ONOpKind>(Kind & 0xff);
+
+ switch (Operand.Etype->Kind) {
+ case OTKUnsigned:
+ case OTKSigned:
+ case OTKBool:
+ switch (Kind) {
+ case ON_Not:
+ Res = LLVMBuildNot(Builder, Operand.Ref, "");
+ break;
+ case ON_Neg_Ov:
+ Res = LLVMBuildNeg(Builder, Operand.Ref, "");
+ break;
+ case ON_Abs_Ov:
+ Res = LLVMBuildSelect
+ (Builder,
+ LLVMBuildICmp (Builder, LLVMIntSLT,
+ Operand.Ref,
+ LLVMConstInt(Operand.Etype->Ref, 0, 0),
+ ""),
+ LLVMBuildNeg(Builder, Operand.Ref, ""),
+ Operand.Ref,
+ "");
+ break;
+ default:
+ llvm_unreachable();
+ }
+ break;
+ case OTKFloat:
+ switch (Kind) {
+ case ON_Neg_Ov:
+ Res = LLVMBuildFNeg(Builder, Operand.Ref, "");
+ break;
+ case ON_Abs_Ov:
+ Res = LLVMBuildSelect
+ (Builder,
+ LLVMBuildFCmp (Builder, LLVMRealOLT,
+ Operand.Ref,
+ LLVMConstReal(Operand.Etype->Ref, 0.0),
+ ""),
+ LLVMBuildFNeg(Builder, Operand.Ref, ""),
+ Operand.Ref,
+ "");
+ break;
+ default:
+ abort();
+ }
+ break;
+ default:
+ abort();
+ }
+ return {Res, Operand.Etype};
+}
+
+static LLVMValueRef
+BuildSMod(LLVMBuilderRef Build, LLVMValueRef L, LLVMValueRef R, const char *s)
+{
+ LLVMTypeRef T = LLVMTypeOf(L);
+ LLVMBasicBlockRef NormalBB;
+ LLVMBasicBlockRef AdjustBB;
+ LLVMBasicBlockRef NextBB;
+ LLVMValueRef PhiVals[3];
+ LLVMBasicBlockRef PhiBB[3];
+
+ NextBB = LLVMAppendBasicBlock(CurrentFunc, "");
+ NormalBB = LLVMAppendBasicBlock(CurrentFunc, "");
+
+ // Avoid overflow with -1
+ // if R = -1 then
+ // result := 0;
+ // else
+ // ...
+ LLVMValueRef Cond;
+ Cond = LLVMBuildICmp(Builder, LLVMIntEQ, R, LLVMConstAllOnes(T), "");
+ LLVMBuildCondBr(Builder, Cond, NextBB, NormalBB);
+ PhiBB[0] = LLVMGetInsertBlock(Builder);
+ PhiVals[0] = LLVMConstNull(T);
+
+ // Rm := Left rem Right
+ LLVMPositionBuilderAtEnd(Builder, NormalBB);
+ LLVMValueRef Rm = LLVMBuildSRem(Builder, L, R, s);
+
+ // if Rm = 0 then
+ // result := 0
+ // else
+ AdjustBB = LLVMAppendBasicBlock(CurrentFunc, "");
+ Cond = LLVMBuildICmp(Builder, LLVMIntEQ, Rm, LLVMConstNull(T), "");
+ LLVMBuildCondBr(Builder, Cond, NextBB, AdjustBB);
+ PhiBB[1] = NormalBB;
+ PhiVals[1] = LLVMConstNull(T);
+
+ // if (L xor R) < 0 then
+ // result := Rm + R
+ // else
+ // result := Rm
+ LLVMPositionBuilderAtEnd(Builder, AdjustBB);
+ LLVMValueRef RXor = LLVMBuildXor(Builder, L, R, "");
+ Cond = LLVMBuildICmp(Builder, LLVMIntSLT, RXor, LLVMConstNull(T), "");
+ LLVMValueRef RmPlusR = LLVMBuildAdd(Builder, Rm, R, "");
+ LLVMValueRef Adj = LLVMBuildSelect(Builder, Cond, RmPlusR, Rm, "");
+ LLVMBuildBr(Builder, NextBB);
+ PhiBB[2] = AdjustBB;
+ PhiVals[2] = Adj;
+
+ // The Phi node
+ LLVMPositionBuilderAtEnd(Builder, NextBB);
+ LLVMValueRef Phi = LLVMBuildPhi(Builder, T, "");
+ LLVMAddIncoming(Phi, PhiVals, PhiBB, 3);
+
+ return Phi;
+}
+
+extern "C" OEnode
+new_dyadic_op (ONOpKind Kind, OEnode Left, OEnode Right)
+{
+ LLVMValueRef Res;
+ LLVMValueRef (*Build)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
+ OTKind ArgKind = Left.Etype->Kind;
+
+ if (Unreach)
+ return { nullptr, Left.Etype};
+
+ // Cannot apply C convention to ON_Op_Kind, so we need to truncate it
+ // (as it is represented by a byte from Ada and by int from C)
+ Kind = static_cast<ONOpKind>(Kind & 0xff);
+
+ switch (ArgKind) {
+ case OTKUnsigned:
+ case OTKSigned:
+ case OTKBool:
+ case OTKEnum:
+ switch (Kind) {
+ case ON_And:
+ Build = &LLVMBuildAnd;
+ break;
+ case ON_Or:
+ Build = &LLVMBuildOr;
+ break;
+ case ON_Xor:
+ Build = &LLVMBuildXor;
+ break;
+
+ case ON_Add_Ov:
+ Build = &LLVMBuildAdd;
+ break;
+ case ON_Sub_Ov:
+ Build = &LLVMBuildSub;
+ break;
+ case ON_Mul_Ov:
+ Build = &LLVMBuildMul;
+ break;
+ case ON_Div_Ov:
+ if (ArgKind == OTKUnsigned)
+ Build = &LLVMBuildUDiv;
+ else
+ Build = &LLVMBuildSDiv;
+ break;
+ case ON_Mod_Ov:
+ if (ArgKind == OTKUnsigned)
+ Build = &LLVMBuildURem;
+ else
+ Build = &BuildSMod;
+ break;
+ case ON_Rem_Ov:
+ if (ArgKind == OTKUnsigned)
+ Build = &LLVMBuildURem;
+ else
+ Build = &LLVMBuildSRem;
+ break;
+ default:
+ abort();
+ }
+ break;
+
+ case OTKFloat:
+ switch (Kind) {
+ case ON_Add_Ov:
+ Build = &LLVMBuildFAdd;
+ break;
+ case ON_Sub_Ov:
+ Build = &LLVMBuildFSub;
+ break;
+ case ON_Mul_Ov:
+ Build = &LLVMBuildFMul;
+ break;
+ case ON_Div_Ov:
+ Build = &LLVMBuildFDiv;
+ break;
+ default:
+ llvm_unreachable();
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ Res = Build(Builder, Left.Ref, Right.Ref, "");
+ return {Res, Left.Etype};
+}
+
+extern "C" OEnode
+new_convert_ov (OEnode Val, OTnode Rtype)
+{
+ if (Unreach) {
+ return {nullptr, Rtype};
+ }
+
+ if (Rtype == Val.Etype) {
+ // Same type, nothing to do
+ return Val;
+ }
+
+ if (Rtype->Ref == Val.Etype->Ref) {
+ // Same undelaying LLVM type. No conversion.
+ return {Val.Ref, Rtype};
+ }
+
+ LLVMValueRef Res;
+
+ switch(Rtype->Kind) {
+ case OTKUnsigned:
+ case OTKSigned:
+ case OTKEnum:
+ case OTKBool:
+ switch(Val.Etype->Kind) {
+ case OTKUnsigned:
+ case OTKSigned:
+ case OTKEnum:
+ case OTKBool:
+ // Int to Int
+ if (static_cast<OTnodeScal*>(Val.Etype)->ScalSize
+ > static_cast<OTnodeScal*>(Rtype)->ScalSize)
+ Res = LLVMBuildTrunc(Builder, Val.Ref, Rtype->Ref, "");
+ else if (static_cast<OTnodeScal*>(Val.Etype)->ScalSize
+ < static_cast<OTnodeScal*>(Rtype)->ScalSize) {
+ if (Val.Etype->Kind == OTKSigned)
+ Res = LLVMBuildSExt(Builder, Val.Ref, Rtype->Ref, "");
+ else
+ Res = LLVMBuildZExt(Builder, Val.Ref, Rtype->Ref, "");
+ } else {
+ Res = LLVMBuildBitCast(Builder, Val.Ref, Rtype->Ref, "");
+ }
+ break;
+ case OTKFloat:
+ // Float to Int
+ {
+ LLVMValueRef V;
+ LLVMValueRef Args[2];
+ Args[0] = Fp0_5;
+ Args[1] = Val.Ref;
+ V = LLVMBuildCall(Builder, CopySignFun, Args, 2, "");
+ V = LLVMBuildFAdd(Builder, V, Val.Ref, "");
+ Res = LLVMBuildFPToSI(Builder, V, Rtype->Ref, "");
+ }
+ break;
+ default:
+ llvm_unreachable();
+ }
+ break;
+ case OTKFloat:
+ // x to Float
+ switch (Val.Etype->Kind) {
+ case OTKSigned:
+ Res = LLVMBuildSIToFP(Builder, Val.Ref, Rtype->Ref, "");
+ break;
+ case OTKUnsigned:
+ Res = LLVMBuildUIToFP(Builder, Val.Ref, Rtype->Ref, "");
+ break;
+ default:
+ abort();
+ }
+ break;
+ case OTKAccess:
+ case OTKIncompleteAccess:
+ assert(LLVMGetTypeKind(LLVMTypeOf(Val.Ref)) == LLVMPointerTypeKind);
+ Res = LLVMBuildBitCast(Builder, Val.Ref, Rtype->Ref, "");
+ break;
+ default:
+ abort();
+ }
+ return {Res, Rtype};
+}
+
+extern "C" OEnode
+new_alloca (OTnode Rtype, OEnode Size)
+{
+ LLVMValueRef Res;
+
+ if (Unreach)
+ Res = nullptr;
+ else {
+ if (CurrentDeclareBlock->StackValue != nullptr
+ && CurrentDeclareBlock->Prev != nullptr) {
+ // Save the stack pointer at the entry of the block.
+ LLVMValueRef FirstInsn =
+ LLVMGetFirstInstruction(CurrentDeclareBlock->StmtBB);
+ LLVMBuilderRef Bld;
+ if (FirstInsn == nullptr) {
+ // Alloca is the first instruction
+ Bld = Builder;
+ } else {
+ LLVMPositionBuilderBefore(ExtraBuilder, FirstInsn);
+ Bld = ExtraBuilder;
+ }
+ CurrentDeclareBlock->StackValue =
+ LLVMBuildCall(Bld, StackSaveFun, nullptr, 0, "");
+ }
+ Res = LLVMBuildArrayAlloca(Builder, LLVMInt8Type(), Size.Ref, "");
+ // Convert
+ Res = LLVMBuildBitCast(Builder, Res, Rtype->Ref, "");
+ }
+ return {Res, Rtype};
+}
+
+extern "C" OCnode
+new_subprogram_address (ODnodeSubprg *Subprg, OTnode Atype)
+{
+ return { LLVMConstBitCast(Subprg->Ref, Atype->Ref), Atype };
+}
+
+struct OGnode {
+ LLVMValueRef Ref;
+ OTnode Gtype;
+};
+
+extern "C" OGnode
+new_global (ODnode Decl)
+{
+ return {Decl->Ref, Decl->Dtype };
+}
+
+extern "C" OGnode
+new_global_selected_element (OGnode Rec, OFnodeBase *El)
+{
+ LLVMValueRef Res;
+
+ switch(El->Kind) {
+ case OF_Record:
+ {
+ LLVMValueRef Idx[2];
+ Idx[0] = LLVMConstInt(LLVMInt32Type(), 0, 0);
+ Idx[1] = LLVMConstInt(LLVMInt32Type(),
+ static_cast<OFnodeRec *>(El)->Index, 0);
+ Res = LLVMConstGEP(Rec.Ref, Idx, 2);
+ }
+ break;
+ case OF_Union:
+ Res = LLVMConstBitCast(Rec.Ref, static_cast<OFnodeUnion *>(El)->PtrType);
+ break;
+ }
+ return {Res, El->FType};
+}
+
+extern "C" OCnode
+new_global_unchecked_address (OGnode Lvalue, OTnode Atype)
+{
+ return {LLVMConstBitCast(Lvalue.Ref, Atype->Ref), Atype};
+}
+
+extern "C" OCnode
+new_global_address (OGnode Lvalue, OTnode Atype)
+{
+ return new_global_unchecked_address(Lvalue, Atype);
+}
+
+struct OLnode {
+ bool Direct;
+ LLVMValueRef Ref;
+ OTnode Ltype;
+};
+
+extern "C" OLnode
+new_obj (ODnode Obj)
+{
+ switch(Obj->getKind()) {
+ case ODKConst:
+ case ODKVar:
+ case ODKLocal:
+ return { false, Obj->Ref, Obj->Dtype };
+ case ODKInterface:
+ return { true, Obj->Ref, Obj->Dtype };
+ case ODKType:
+ case ODKSubprg:
+ llvm_unreachable();
+ }
+}
+
+extern "C" OEnode
+new_value (OLnode *Lvalue)
+{
+ LLVMValueRef Res;
+
+ if (Unreach)
+ Res = nullptr;
+ else {
+ if (Lvalue->Direct)
+ Res = Lvalue->Ref;
+ else
+ Res = LLVMBuildLoad(Builder, Lvalue->Ref, "");
+ }
+ return {Res, Lvalue->Ltype };
+}
+
+extern "C" OEnode
+new_obj_value (ODnode Obj)
+{
+ OLnode t = new_obj(Obj);
+ return new_value (&t);
+}
+
+extern "C" OLnode
+new_indexed_element (OLnode *Arr, OEnode Index)
+{
+ LLVMValueRef Idx[2];
+ LLVMValueRef Res;
+
+ if (Unreach)
+ Res = nullptr;
+ else {
+ Idx[0] = LLVMConstInt(LLVMInt32Type(), 0, 0);
+ Idx[1] = Index.Ref;
+ Res = LLVMBuildGEP(Builder, Arr->Ref, Idx, 2, "");
+ }
+ return { false, Res, static_cast<OTnodeArr *>(Arr->Ltype)->ElType };
+}
+
+extern "C" OLnode
+new_slice (OLnode *Arr, OTnode Rtype, OEnode Index)
+{
+ LLVMValueRef Idx[2];
+ LLVMValueRef Res;
+
+ if (Unreach)
+ Res = nullptr;
+ else {
+ Idx[0] = LLVMConstInt(LLVMInt32Type(), 0, 0);
+ Idx[1] = Index.Ref;
+ Res = LLVMBuildGEP(Builder, Arr->Ref, Idx, 2, "");
+ Res = LLVMBuildBitCast(Builder, Res, LLVMPointerType(Rtype->Ref, 0), "");
+ }
+ return { false, Res, Rtype};
+}
+
+extern "C" OLnode
+new_selected_element (OLnode *Rec, OFnodeBase *El)
+{
+ LLVMValueRef Res;
+
+ if (Unreach)
+ Res = nullptr;
+ else {
+ switch(El->Kind) {
+ case OF_Record:
+ {
+ LLVMValueRef Idx[2];
+ Idx[0] = LLVMConstInt(LLVMInt32Type(), 0, 0);
+ Idx[1] = LLVMConstInt(LLVMInt32Type(),
+ static_cast<OFnodeRec *>(El)->Index, 0);
+ Res = LLVMBuildGEP(Builder, Rec->Ref, Idx, 2, "");
+ }
+ break;
+ case OF_Union:
+ Res = LLVMBuildBitCast(Builder, Rec->Ref,
+ static_cast<OFnodeUnion *>(El)->PtrType, "");
+ break;
+ }
+ }
+ return { false, Res, El->FType };
+}
+
+extern "C" OLnode
+new_access_element (OEnode Acc)
+{
+ LLVMValueRef Res;
+
+ switch(Acc.Etype->Kind) {
+ case OTKAccess:
+ Res = Acc.Ref;
+ break;
+ case OTKIncompleteAccess:
+ // Unwrap the structure
+ {
+ LLVMValueRef Idx[2];
+
+ Idx[0] = LLVMConstInt(LLVMInt32Type(), 0, 0);
+ Idx[1] = LLVMConstInt(LLVMInt32Type(), 0, 0);
+ Res = LLVMBuildGEP(Builder, Acc.Ref, Idx, 2, "");
+ }
+ break;
+ default:
+ llvm_unreachable();
+ }
+ return {false, Res, static_cast<OTnodeAccBase *>(Acc.Etype)->Acc };
+}
+
+extern "C" OEnode
+new_unchecked_address (OLnode *Lvalue, OTnode Atype)
+{
+ LLVMValueRef Res;
+
+ if (Unreach)
+ Res = nullptr;
+ else
+ Res = LLVMBuildBitCast(Builder, Lvalue->Ref, Atype->Ref, "");
+ return {Res, Atype};
+}
+
+extern "C" OEnode
+new_address (OLnode *Lvalue, OTnode Atype)
+{
+ return new_unchecked_address(Lvalue, Atype);
+}
+
+extern "C" void
+new_assign_stmt (OLnode *Target, OEnode Value)
+{
+ assert (!Targ->Direct);
+ if (!Unreach) {
+ LLVMBuildStore(Builder, Value.Ref, Target->Ref);
+ }
+}
+
+extern "C" void
+new_debug_line_decl (unsigned Line)
+{
+ DebugCurrentLine = Line;
+}
+
+extern "C" void
+new_debug_line_stmt (unsigned Line)
+{
+ DebugCurrentLine = Line;
+}