diff --git a/.gitignore b/.gitignore index f105ccb..0583728 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .idea/ .vscode/ +.vscode/ + cmake-build-*/ build/ diff --git a/include/IR/CallingConv.h b/include/IR/CallingConv.h new file mode 100644 index 0000000..dbe67ab --- /dev/null +++ b/include/IR/CallingConv.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace CallingConv { + void Init(Napi::Env env, Napi::Object &exports); +} diff --git a/include/IR/Function.h b/include/IR/Function.h index 0fd7e26..ddfa3f8 100644 --- a/include/IR/Function.h +++ b/include/IR/Function.h @@ -64,6 +64,10 @@ class Function : public Napi::ObjectWrap { Napi::Value getType(const Napi::CallbackInfo &info); + Napi::Value getCallingConv(const Napi::CallbackInfo &info); + + void setCallingConv(const Napi::CallbackInfo &info); + void addFnAttr(const Napi::CallbackInfo &info); void addParamAttr(const Napi::CallbackInfo &info); diff --git a/include/IR/PassManager.h b/include/IR/PassManager.h new file mode 100644 index 0000000..5d91f1f --- /dev/null +++ b/include/IR/PassManager.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include +#include "llvm/Pass.h" +#include "llvm/Passes/PassBuilder.h" + +class ModulePassManager : public Napi::ObjectWrap { +public: + static inline Napi::FunctionReference constructor; // NOLINT + + static void Init(Napi::Env env, Napi::Object &exports); + + static bool IsClassOf(const Napi::Value &value); + + static llvm::ModulePassManager *Extract(const Napi::Value &value); + + explicit ModulePassManager(const Napi::CallbackInfo &info); + + llvm::ModulePassManager *getLLVMPrimitive(); + +private: + llvm::ModulePassManager *passManager; + + llvm::PassBuilder *passBuilder; + llvm::ModuleAnalysisManager *moduleAnalysisManager; + llvm::LoopAnalysisManager *loopAnalysisManager; + llvm::FunctionAnalysisManager *functionAnalysisManager; + llvm::CGSCCAnalysisManager *cgsccAnalysisManager; + + llvm::OptimizationLevel level; + + Napi::Value createFunctionPassManager(const Napi::CallbackInfo &info); + void addFunctionPasses(const Napi::CallbackInfo &info); + void addVerifierPass(const Napi::CallbackInfo &info); + Napi::Value isEmpty(const Napi::CallbackInfo &info); + + void run(const Napi::CallbackInfo &info); +}; + +class FunctionPassManager : public Napi::ObjectWrap { +public: + static inline Napi::FunctionReference constructor; // NOLINT + + static void Init(Napi::Env env, Napi::Object &exports); + + static Napi::Object New(Napi::Env env, llvm::FunctionPassManager *fpm); + + static bool IsClassOf(const Napi::Value &value); + + static llvm::FunctionPassManager *Extract(const Napi::Value &value); + + explicit FunctionPassManager(const Napi::CallbackInfo &info); + + llvm::FunctionPassManager *getLLVMPrimitive(); + +private: + llvm::FunctionPassManager *passManager = nullptr; + + void addSROAPass(const Napi::CallbackInfo &info); + void addEarlyCSEPass(const Napi::CallbackInfo &info); + void addInstCombinePass(const Napi::CallbackInfo &info); + + Napi::Value isEmpty(const Napi::CallbackInfo &info); +}; diff --git a/include/IR/index.h b/include/IR/index.h index e3b4f5c..fb8ec99 100644 --- a/include/IR/index.h +++ b/include/IR/index.h @@ -16,6 +16,7 @@ #include "IR/GlobalObject.h" #include "IR/GlobalVariable.h" #include "IR/Function.h" +#include "IR/CallingConv.h" #include "IR/Instruction.h" #include "IR/Instructions.h" #include "IR/IRBuilder.h" @@ -26,5 +27,6 @@ #include "IR/DataLayout.h" #include "IR/Verifier.h" #include "IR/Intrinsic.h" +#include "IR/PassManager.h" void InitIR(Napi::Env env, Napi::Object &exports); diff --git a/include/Passes/OptimizationLevel.h b/include/Passes/OptimizationLevel.h new file mode 100644 index 0000000..bbd457a --- /dev/null +++ b/include/Passes/OptimizationLevel.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace OptimizationLevel { + enum Level { + O0, + O1, + O2, + O3, + Os, + Oz, + }; + + void Init(Napi::Env env, Napi::Object &exports); +} diff --git a/include/Passes/ThinOrFullLTOPhase.h b/include/Passes/ThinOrFullLTOPhase.h new file mode 100644 index 0000000..2657305 --- /dev/null +++ b/include/Passes/ThinOrFullLTOPhase.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace ThinOrFullLTOPhase { + void Init(Napi::Env env, Napi::Object &exports); +} diff --git a/include/Passes/index.h b/include/Passes/index.h new file mode 100644 index 0000000..ecf3123 --- /dev/null +++ b/include/Passes/index.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include "Passes/OptimizationLevel.h" +#include "Passes/ThinOrFullLTOPhase.h" + +void InitPasses(Napi::Env env, Napi::Object &exports); diff --git a/include/Support/CodeGen.h b/include/Support/CodeGen.h new file mode 100644 index 0000000..7be6b7e --- /dev/null +++ b/include/Support/CodeGen.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace CodeGen { + void Init(Napi::Env env, Napi::Object &exports); +} diff --git a/include/Support/index.h b/include/Support/index.h index 86e9cb6..a239600 100644 --- a/include/Support/index.h +++ b/include/Support/index.h @@ -3,5 +3,6 @@ #include #include "Support/SourceMgr.h" #include "Support/TargetSelect.h" +#include "Support/CodeGen.h" void InitSupport(Napi::Env env, Napi::Object &exports); diff --git a/include/Target/TargetMachine.h b/include/Target/TargetMachine.h index a5dfdb4..22b9f1b 100644 --- a/include/Target/TargetMachine.h +++ b/include/Target/TargetMachine.h @@ -19,4 +19,6 @@ class TargetMachine : public Napi::ObjectWrap { const llvm::TargetMachine *targetMachine = nullptr; Napi::Value createDataLayout(const Napi::CallbackInfo &info); + void emitToFile(const Napi::CallbackInfo &info); + Napi::Value emitToBuffer(const Napi::CallbackInfo &info); }; diff --git a/include/Util/ErrMsg.h b/include/Util/ErrMsg.h index dbc6854..e9b7c93 100644 --- a/include/Util/ErrMsg.h +++ b/include/Util/ErrMsg.h @@ -3,7 +3,7 @@ namespace ErrMsg { namespace Class { namespace APInt { - constexpr const char *constructor = "APInt.constructor needs to be called with new (numBits: number, value: number, isSigned?: boolean)"; + constexpr const char *constructor = "APInt.constructor needs to be called with new (numBits: number, value: number | bigint, isSigned?: boolean)"; } namespace APFloat { constexpr const char *constructor = "APFloat.constructor needs to be called with new (value: number)"; @@ -55,7 +55,8 @@ namespace ErrMsg { constexpr const char *constructor = "StructType.constructor needs to be called with new (external: Napi::External)"; constexpr const char *create = "StructType.create needs to be called with:" "\n\t - (context: LLVMContext, name: string)" - "\n\t - (context: LLVMContext, elementTypes: Type[], name: string)"; + "\n\t - (context: LLVMContext, elementTypes: Type[], name: string)" + "\n\t - (context: LLVMContext, elementTypes: Type[], name: string, isPacked: boolean = false)"; constexpr const char *get = "StructType.get needs to be called with:" "\n\t - (context: LLVMContext)" "\n\t - (context: LLVMContext, elementTypes: Type[])"; @@ -175,6 +176,7 @@ namespace ErrMsg { constexpr const char *insertAfter = "Function.insertAfter needs to be called with (where: BasicBlock, basicBlock: BasicBlock)"; constexpr const char *setPersonalityFn = "Function.setPersonalityFn needs to be called with (fn: Constant)"; constexpr const char *setSubprogram = "Function.setSubprogram needs to be called with (subprogram: DISubprogram)"; + constexpr const char *setCallingConv = "Function.setCallingConv needs to bu called with (cc: CallingConv)"; constexpr const char *addFnAttr = "Function.addFnAttr needs to be called with" "\n\t - (kind: Attribute.AttrKind)" "\n\t - (kind: string, value: string = "")" @@ -565,13 +567,31 @@ namespace ErrMsg { } namespace Target { constexpr const char *constructor = "Target.constructor needs to be called with new (external: Napi::External)"; - constexpr const char *createTargetMachine = "Target.createTargetMachine needs to be called with (targetTriple: string, cpu: string, features?: string)"; + constexpr const char *createTargetMachine = "Target.createTargetMachine needs to be called with:" + "\n\t -(targetTriple: string, cpu: string, features?: string)" + "\n\t -(targetTriple: string, cpu: string, features: string, reloc: Reloc)" + "\n\t -(targetTriple: string, cpu: string, features: string, reloc: Reloc, codeModel: CodeModel)" + "\n\t -(targetTriple: string, cpu: string, features: string, reloc: Reloc, codeModel: CodeModel, codeGenOpt: CodeGenOpt)" + "\n\t -(targetTriple: string, cpu: string, features: string, reloc: Reloc, codeModel: CodeModel, codeGenOpt: CodeGenOpt, jit: boolean)"; } namespace TargetRegistry { constexpr const char *lookupTarget = "TargetRegistry.lookupTarget needs to be called with (triple: string)"; } namespace TargetMachine { constexpr const char *constructor = "TargetMachine.constructor needs to be called with new (external: Napi::External)"; + constexpr const char *emitToFile = "TargetMachine.emitToFile needs to be called with (module: Module, path: string, format: CodeGenFileType)"; + constexpr const char *emitToBuffer = "TargetMachine.emitToBuffer needs to be called with (module: Module, format: CodeGenFileType)"; + constexpr const char *addPassesToEmitFile = "Cannot register passes to emit file. Check registered ASM printers, targets and relevant stuff"; + } + namespace ModulePassManager { + constexpr const char *constructor = "ModulePassManager.constructor needs to be called with new (optLevel: OptimizationLevel)"; + constexpr const char *createFunctionPassManager = "ModulePassManager.createFunctionPassManager needs to be called with new (ltoPhase: ThinOrFullLTOPhase)"; + constexpr const char *addFunctionPasses = "ModulePassManager.addFunctionPasses needs to be called with new (fpm: FunctionPassManager)"; + constexpr const char *run = "ModulePassManager.run needs to be called with new (module: Module)"; + } + namespace FunctionPassManager { + constexpr const char *constructor = "ModulePassManager.constructor needs to be called with new (external: Napi::External)"; + } } namespace Namespace::Intrinsic { diff --git a/llvm-bindings.d.ts b/llvm-bindings.d.ts index ca993af..3337164 100644 --- a/llvm-bindings.d.ts +++ b/llvm-bindings.d.ts @@ -1,6 +1,6 @@ declare namespace llvm { class APInt { - public constructor(numBits: number, value: number, isSigned?: boolean); + public constructor(numBits: number, value: number | bigint, isSigned?: boolean); } class APFloat { @@ -424,6 +424,7 @@ declare namespace llvm { class StructType extends Type { public static create(context: LLVMContext, name: string): StructType; public static create(context: LLVMContext, elementTypes: Type[], name: string): StructType; + public static create(context: LLVMContext, elementTypes: Type[], name: string, isPacked?: boolean): StructType; public static get(context: LLVMContext): StructType; public static get(context: LLVMContext, elementTypes: Type[]): StructType; @@ -812,6 +813,10 @@ declare namespace llvm { public getSubprogram(): DISubprogram; + public getCallingConv(): number; + + public setCallingConv(cc: number): void; + // duplicated public getType(): PointerType; @@ -827,6 +832,126 @@ declare namespace llvm { protected constructor(); } + enum CallingConv { + C = 0, + Fast = 8, + Cold = 9, + GHC = 10, + HiPE = 11, + WebKit_JS = 12, + AnyReg = 13, + PreserveMost = 14, + PreserveAll = 15, + Swift = 16, + CXX_FAST_TLS = 17, + Tail = 18, + CFGuard_Check = 19, + SwiftTail = 20, + FirstTargetCC = 64, + X86_StdCall = 64, + X86_FastCall = 65, + ARM_APCS = 66, + ARM_AAPCS = 67, + ARM_AAPCS_VFP = 68, + MSP430_INTR = 69, + X86_ThisCall = 70, + PTX_Kernel = 71, + PTX_Device = 72, + SPIR_FUNC = 75, + SPIR_KERNEL = 76, + Intel_OCL_BI = 77, + X86_64_SysV = 78, + Win64 = 79, + X86_VectorCall = 80, + HHVM = 81, + HHVM_C = 82, + X86_INTR = 83, + AVR_INTR = 84, + AVR_SIGNAL = 85, + AVR_BUILTIN = 86, + AMDGPU_VS = 87, + AMDGPU_GS = 88, + AMDGPU_PS = 89, + AMDGPU_CS = 90, + AMDGPU_KERNEL = 91, + X86_RegCall = 92, + AMDGPU_HS = 93, + MSP430_BUILTIN = 94, + AMDGPU_LS = 95, + AMDGPU_ES = 96, + AArch64_VectorCall = 97, + AArch64_SVE_VectorCall = 98, + WASM_EmscriptenInvoke = 99, + AMDGPU_Gfx = 100, + M68k_INTR = 101, + } + + enum Reloc { + Static = 0, + PIC_ = 1, + DynamicNoPIC = 2, + ROPI = 3, + RWPI = 4, + ROPI_RWPI = 5, + } + + enum CodeModel { + Tiny = 0, + Small = 1, + Kernel = 2, + Medium = 3, + Large = 4, + } + + enum PICLevel { + NotPIC = 0, + SmallPIC = 1, + BigPIC = 2, + } + + enum PIELevel { + Default = 0, + Small = 1, + Large = 2, + } + + enum TLSModel { + GeneralDynamic = 0, + LocalDynamic = 1, + InitialExec = 2, + LocalExec = 3, + } + + enum CodeGenOpt { + None = 0, + Less = 1, + Default = 2, + Aggressive = 3, + } + + enum CodeGenFileType { + Assembly = 0, + Object = 1, + Null = 2, + } + + enum OptimizationLevel { + O0, + O1, + O2, + O3, + Os, + Oz, + } + + enum ThinOrFullLTOPhase { + None, + ThinLTOPreLink, + ThinLTOPostLink, + FullLTOPreLink, + FullLTOPostLink + } + class Instruction extends User { public user_back(): Instruction | null; @@ -2183,6 +2308,9 @@ declare namespace llvm { class Target { public createTargetMachine(targetTriple: string, cpu: string, features?: string): TargetMachine; + public createTargetMachine(targetTriple: string, cpu: string, features: string, reloc: Reloc): TargetMachine; + public createTargetMachine(targetTriple: string, cpu: string, features: string, reloc: Reloc, codeModel: CodeModel): TargetMachine; + public createTargetMachine(targetTriple: string, cpu: string, features: string, reloc: Reloc, codeModel: CodeModel, codeGenOpt: CodeGenOpt): TargetMachine; public getName(): string; @@ -2203,10 +2331,29 @@ declare namespace llvm { class TargetMachine { public createDataLayout(): DataLayout; + public emitToFile(module: Module, path: string, format: CodeGenFileType): void; + public emitToBuffer(module: Module, format: CodeGenFileType): Buffer; protected constructor(); } + class FunctionPassManager { + constructor(); + + addSROAPass(): void; + addEarlyCSEPass(useSSA?: boolean): void; + addInstCombinePass(): void; + } + + class ModulePassManager { + constructor(level: OptimizationLevel); + + createFunctionPassManager(lto: ThinOrFullLTOPhase): FunctionPassManager; + addFunctionPasses(fpm: FunctionPassManager): void; + addVerifierPass(): void; + run(module: Module): void; + } + function InitializeAllTargetInfos(): void; function InitializeAllTargets(): void; diff --git a/src/ADT/APInt.cpp b/src/ADT/APInt.cpp index 6262385..3b250b2 100644 --- a/src/ADT/APInt.cpp +++ b/src/ADT/APInt.cpp @@ -20,11 +20,14 @@ llvm::APInt &APInt::Extract(const Napi::Value &value) { APInt::APInt(const Napi::CallbackInfo &info) : ObjectWrap(info) { Napi::Env env = info.Env(); unsigned argsLen = info.Length(); - if (!info.IsConstructCall() || argsLen < 2 || !info[0].IsNumber() || !info[1].IsNumber() || argsLen >= 3 && !info[2].IsBoolean()) { + if (!info.IsConstructCall() || argsLen < 2 || !info[0].IsNumber() || !(info[1].IsNumber() || info[1].IsBigInt()) || argsLen >= 3 && !info[2].IsBoolean()) { throw Napi::TypeError::New(env, ErrMsg::Class::APInt::constructor); } unsigned numBits = info[0].As(); - uint64_t val = info[1].As().Int64Value(); + bool lossless = true; + uint64_t val = info[1].IsNumber() + ? info[1].As().Int64Value() + : info[1].As().Int64Value(&lossless); bool isSigned = false; if (argsLen >= 3) { isSigned = info[2].As(); diff --git a/src/IR/CallingConv.cpp b/src/IR/CallingConv.cpp new file mode 100644 index 0000000..2d9ff6f --- /dev/null +++ b/src/IR/CallingConv.cpp @@ -0,0 +1,57 @@ +#include "IR/index.h" + +void CallingConv::Init(Napi::Env env, Napi::Object &exports) { + Napi::Object callingConvNS = Napi::Object::New(env); + callingConvNS.Set("C", Napi::Number::New(env, llvm::CallingConv::C)); + callingConvNS.Set("Fast", Napi::Number::New(env, llvm::CallingConv::Fast)); + callingConvNS.Set("Cold", Napi::Number::New(env, llvm::CallingConv::Cold)); + callingConvNS.Set("GHC", Napi::Number::New(env, llvm::CallingConv::GHC)); + callingConvNS.Set("HiPE", Napi::Number::New(env, llvm::CallingConv::HiPE)); + callingConvNS.Set("WebKit_JS", Napi::Number::New(env, llvm::CallingConv::WebKit_JS)); + callingConvNS.Set("AnyReg", Napi::Number::New(env, llvm::CallingConv::AnyReg)); + callingConvNS.Set("PreserveMost", Napi::Number::New(env, llvm::CallingConv::PreserveMost)); + callingConvNS.Set("PreserveAll", Napi::Number::New(env, llvm::CallingConv::PreserveAll )); + callingConvNS.Set("Swift", Napi::Number::New(env, llvm::CallingConv::Swift)); + callingConvNS.Set("CXX_FAST_TLS", Napi::Number::New(env, llvm::CallingConv::CXX_FAST_TLS)); + callingConvNS.Set("Tail", Napi::Number::New(env, llvm::CallingConv::Tail)); + callingConvNS.Set("CFGuard_Check", Napi::Number::New(env, llvm::CallingConv::CFGuard_Check)); + callingConvNS.Set("SwiftTail", Napi::Number::New(env, llvm::CallingConv::SwiftTail)); + callingConvNS.Set("FirstTargetCC", Napi::Number::New(env, llvm::CallingConv::FirstTargetCC)); + callingConvNS.Set("X86_StdCall", Napi::Number::New(env, llvm::CallingConv::X86_StdCall)); + callingConvNS.Set("X86_FastCall", Napi::Number::New(env, llvm::CallingConv::X86_FastCall)); + callingConvNS.Set("ARM_APCS", Napi::Number::New(env, llvm::CallingConv::ARM_APCS)); + callingConvNS.Set("ARM_AAPCS", Napi::Number::New(env, llvm::CallingConv::ARM_AAPCS)); + callingConvNS.Set("ARM_AAPCS_VFP", Napi::Number::New(env, llvm::CallingConv::ARM_AAPCS_VFP)); + callingConvNS.Set("MSP430_INTR", Napi::Number::New(env, llvm::CallingConv::MSP430_INTR)); + callingConvNS.Set("X86_ThisCall", Napi::Number::New(env, llvm::CallingConv::X86_ThisCall)); + callingConvNS.Set("PTX_Kernel", Napi::Number::New(env, llvm::CallingConv::PTX_Kernel)); + callingConvNS.Set("PTX_Device", Napi::Number::New(env, llvm::CallingConv::PTX_Device)); + callingConvNS.Set("SPIR_FUNC", Napi::Number::New(env, llvm::CallingConv::SPIR_FUNC)); + callingConvNS.Set("SPIR_KERNEL", Napi::Number::New(env, llvm::CallingConv::SPIR_KERNEL)); + callingConvNS.Set("Intel_OCL_BI", Napi::Number::New(env, llvm::CallingConv::Intel_OCL_BI)); + callingConvNS.Set("X86_64_SysV", Napi::Number::New(env, llvm::CallingConv::X86_64_SysV)); + callingConvNS.Set("Win64", Napi::Number::New(env, llvm::CallingConv::Win64)); + callingConvNS.Set("X86_VectorCall", Napi::Number::New(env, llvm::CallingConv::X86_VectorCall)); + callingConvNS.Set("HHVM", Napi::Number::New(env, llvm::CallingConv::HHVM)); + callingConvNS.Set("HHVM_C", Napi::Number::New(env, llvm::CallingConv::HHVM_C)); + callingConvNS.Set("X86_INTR", Napi::Number::New(env, llvm::CallingConv::X86_INTR)); + callingConvNS.Set("AVR_INTR", Napi::Number::New(env, llvm::CallingConv::AVR_INTR)); + callingConvNS.Set("AVR_SIGNAL", Napi::Number::New(env, llvm::CallingConv::AVR_SIGNAL)); + callingConvNS.Set("AVR_BUILTIN", Napi::Number::New(env, llvm::CallingConv::AVR_BUILTIN)); + callingConvNS.Set("AMDGPU_VS", Napi::Number::New(env, llvm::CallingConv::AMDGPU_VS)); + callingConvNS.Set("AMDGPU_GS", Napi::Number::New(env, llvm::CallingConv::AMDGPU_GS)); + callingConvNS.Set("AMDGPU_PS", Napi::Number::New(env, llvm::CallingConv::AMDGPU_PS)); + callingConvNS.Set("AMDGPU_CS", Napi::Number::New(env, llvm::CallingConv::AMDGPU_CS)); + callingConvNS.Set("AMDGPU_KERNEL", Napi::Number::New(env, llvm::CallingConv::AMDGPU_KERNEL)); + callingConvNS.Set("X86_RegCall", Napi::Number::New(env, llvm::CallingConv::X86_RegCall)); + callingConvNS.Set("AMDGPU_HS", Napi::Number::New(env, llvm::CallingConv::AMDGPU_HS)); + callingConvNS.Set("MSP430_BUILTIN", Napi::Number::New(env, llvm::CallingConv::MSP430_BUILTIN)); + callingConvNS.Set("AMDGPU_LS", Napi::Number::New(env, llvm::CallingConv::AMDGPU_LS)); + callingConvNS.Set("AMDGPU_ES", Napi::Number::New(env, llvm::CallingConv::AMDGPU_ES)); + callingConvNS.Set("AArch64_VectorCall", Napi::Number::New(env, llvm::CallingConv::AArch64_VectorCall)); + callingConvNS.Set("AArch64_SVE_VectorCall", Napi::Number::New(env, llvm::CallingConv::AArch64_SVE_VectorCall)); + callingConvNS.Set("WASM_EmscriptenInvoke", Napi::Number::New(env, llvm::CallingConv::WASM_EmscriptenInvoke)); + callingConvNS.Set("AMDGPU_Gfx", Napi::Number::New(env, llvm::CallingConv::AMDGPU_Gfx)); + callingConvNS.Set("M68k_INTR", Napi::Number::New(env, llvm::CallingConv::M68k_INTR)); + exports.Set("CallingConv", callingConvNS); +} \ No newline at end of file diff --git a/src/IR/DerivedTypes.cpp b/src/IR/DerivedTypes.cpp index ee573d2..07b2333 100644 --- a/src/IR/DerivedTypes.cpp +++ b/src/IR/DerivedTypes.cpp @@ -268,27 +268,49 @@ llvm::StructType *StructType::getLLVMPrimitive() { return structType; } +inline bool isOpaqueNamedStructArgs(const Napi::CallbackInfo &info, unsigned argsLen) { + return argsLen == 2 && LLVMContext::IsClassOf(info[0]) && info[1].IsString(); +} + +inline bool isNamedStructArgs(const Napi::CallbackInfo &info, unsigned argsLen) { + return argsLen == 3 && LLVMContext::IsClassOf(info[0]) && info[1].IsArray() && info[2].IsString(); +} + +inline bool isPackedNamedStructArgs(const Napi::CallbackInfo &info, unsigned argsLen) { + return argsLen == 4 && LLVMContext::IsClassOf(info[0]) && info[1].IsArray() && info[2].IsString() && info[3].IsBoolean(); +} + Napi::Value StructType::create(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); unsigned argsLen = info.Length(); - if (!(argsLen == 2 && LLVMContext::IsClassOf(info[0]) && info[1].IsString()) && - !(argsLen >= 3 && LLVMContext::IsClassOf(info[0]) && info[1].IsArray() && info[2].IsString())) { + + auto isOpaqueNamedStruct = isOpaqueNamedStructArgs(info, argsLen); + auto isNamedStruct = isNamedStructArgs(info, argsLen); + auto isPackedNamedStruct = isPackedNamedStructArgs(info, argsLen); + + if (!(isOpaqueNamedStruct || isNamedStruct || isPackedNamedStruct)) { throw Napi::TypeError::New(env, ErrMsg::Class::StructType::create); } + llvm::LLVMContext &context = LLVMContext::Extract(info[0]); const std::string &name = info[argsLen == 2 ? 1 : 2].As(); llvm::StructType *structType; - if (argsLen >= 3) { + if (isNamedStruct || isPackedNamedStruct) { auto eleTypesArray = info[1].As(); unsigned numElements = eleTypesArray.Length(); std::vector elementTypes(numElements); for (unsigned i = 0; i < numElements; ++i) { elementTypes[i] = Type::Extract(eleTypesArray.Get(i)); } - structType = llvm::StructType::create(context, elementTypes, name); + bool isPacked = false; + if (isPackedNamedStruct) { + isPacked = info[3].As(); + } + structType = llvm::StructType::create(context, elementTypes, name, isPacked); } else { structType = llvm::StructType::create(context, name); } + return StructType::New(env, structType); } diff --git a/src/IR/Function.cpp b/src/IR/Function.cpp index dcd9412..a2c9ae9 100644 --- a/src/IR/Function.cpp +++ b/src/IR/Function.cpp @@ -25,6 +25,9 @@ void Function::Init(Napi::Env env, Napi::Object &exports) { InstanceMethod("setSubprogram", &Function::setSubprogram), InstanceMethod("getSubprogram", &Function::getSubprogram), InstanceMethod("getType", &Function::getType), + InstanceMethod("getCallingConv", &Function::getCallingConv), + InstanceMethod("setCallingConv", &Function::setCallingConv), + InstanceMethod("getType", &Function::getType), InstanceMethod("addFnAttr", &Function::addFnAttr), InstanceMethod("addParamAttr", &Function::addParamAttr), InstanceMethod("addRetAttr", &Function::addRetAttr) @@ -198,6 +201,22 @@ Napi::Value Function::getType(const Napi::CallbackInfo &info) { return PointerType::New(env, type); } +Napi::Value Function::getCallingConv(const Napi::CallbackInfo &info) +{ + return Napi::Number::New(info.Env(), static_cast(function->getCallingConv())); +} + +void Function::setCallingConv(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + if (info.Length() == 1 && info[0].IsNumber()) { + llvm::CallingConv::ID cc = static_cast(info[0].As().Uint32Value()); + function->setCallingConv(cc); + return; + } + throw Napi::TypeError::New(env, ErrMsg::Class::Function::setCallingConv); +} + void Function::addFnAttr(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); diff --git a/src/IR/PassManager.cpp b/src/IR/PassManager.cpp new file mode 100644 index 0000000..cfc57e6 --- /dev/null +++ b/src/IR/PassManager.cpp @@ -0,0 +1,223 @@ +#include "IR/index.h" +#include "Util/index.h" + +#include "Passes/OptimizationLevel.h" + +#include "llvm/Pass.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/CodeGen/CodeGenPassBuilder.h" +#include "llvm/Transforms/Scalar/SROA.h" +#include "llvm/Transforms/Scalar/EarlyCSE.h" +#include "llvm/Transforms/InstCombine/InstCombine.h" + +//===----------------------------------------------------------------------===// +// ModulePassManager Class +//===----------------------------------------------------------------------===// + +void ModulePassManager::Init(Napi::Env env, Napi::Object &exports) { + Napi::HandleScope scope(env); + Napi::Function func = DefineClass(env, "ModulePassManager", { + InstanceMethod("createFunctionPassManager", &ModulePassManager::createFunctionPassManager), + InstanceMethod("addFunctionPasses", &ModulePassManager::addFunctionPasses), + InstanceMethod("addVerifierPass", &ModulePassManager::addVerifierPass), + InstanceMethod("isEmpty", &ModulePassManager::isEmpty), + InstanceMethod("run", &ModulePassManager::run), + }); + constructor = Napi::Persistent(func); + constructor.SuppressDestruct(); + Inherit(env, constructor.Value(), GlobalObject::constructor.Value()); + exports.Set("ModulePassManager", func); +} + +bool ModulePassManager::IsClassOf(const Napi::Value &value) { + return value.IsNull() || value.As().InstanceOf(constructor.Value()); +} + +llvm::ModulePassManager *ModulePassManager::Extract(const Napi::Value &value) { + if (value.IsNull()) { + return nullptr; + } + return Unwrap(value.As())->getLLVMPrimitive(); +} + +llvm::OptimizationLevel mapToLLVM(OptimizationLevel::Level opt) { + switch (opt) { + case OptimizationLevel::O1: return llvm::OptimizationLevel::O1; + case OptimizationLevel::O2: return llvm::OptimizationLevel::O2; + case OptimizationLevel::O3: return llvm::OptimizationLevel::O3; + case OptimizationLevel::Os: return llvm::OptimizationLevel::Os; + case OptimizationLevel::Oz: return llvm::OptimizationLevel::Oz; + default: return llvm::OptimizationLevel::O0; + } +} + +ModulePassManager::ModulePassManager(const Napi::CallbackInfo &info) : ObjectWrap(info) { + Napi::Env env = info.Env(); + if (info.IsConstructCall() && info.Length() == 1 && info[0].IsNumber()) { + level = mapToLLVM((OptimizationLevel::Level) info[0].As().Uint32Value()); + + passBuilder = new llvm::PassBuilder(); + + moduleAnalysisManager = new llvm::ModuleAnalysisManager(); + cgsccAnalysisManager = new llvm::CGSCCAnalysisManager(); + functionAnalysisManager = new llvm::FunctionAnalysisManager(); + loopAnalysisManager = new llvm::LoopAnalysisManager (); + + passBuilder->registerModuleAnalyses(*moduleAnalysisManager); + passBuilder->registerCGSCCAnalyses(*cgsccAnalysisManager); + passBuilder->registerFunctionAnalyses(*functionAnalysisManager); + passBuilder->registerLoopAnalyses(*loopAnalysisManager); + passBuilder->crossRegisterProxies( + *loopAnalysisManager, + *functionAnalysisManager, + *cgsccAnalysisManager, + *moduleAnalysisManager + ); + + if (level == llvm::OptimizationLevel::O0) { + passManager = new llvm::ModulePassManager( + passBuilder->buildO0DefaultPipeline(level) + ); + return; + } + + passManager = new llvm::ModulePassManager( + passBuilder->buildPerModuleDefaultPipeline(level) + ); + return; + } + throw Napi::TypeError::New(env, ErrMsg::Class::ModulePassManager::constructor); +} + +llvm::ModulePassManager *ModulePassManager::getLLVMPrimitive() { + return passManager; +} + +Napi::Value ModulePassManager::createFunctionPassManager(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + unsigned argsLen = info.Length(); + + if (argsLen != 1 || !info[0].IsNumber()) { + throw Napi::TypeError::New(env, ErrMsg::Class::ModulePassManager::createFunctionPassManager); + } + + auto lto = (llvm::ThinOrFullLTOPhase) info[0].As().Uint32Value(); + + auto fpm = new llvm::FunctionPassManager( + passBuilder->buildFunctionSimplificationPipeline(level, lto) + ); + + return FunctionPassManager::New(env, fpm); +} + +void ModulePassManager::addVerifierPass(const Napi::CallbackInfo& info) +{ + passManager->addPass(llvm::VerifierPass()); +} + +void ModulePassManager::addFunctionPasses(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + auto argsLen = info.Length(); + + if (argsLen != 1 || !FunctionPassManager::IsClassOf(info[0])) { + throw Napi::TypeError::New(env, ErrMsg::Class::ModulePassManager::addFunctionPasses); + } + + auto fpm = FunctionPassManager::Extract(info[0]); + + if (!fpm) { + throw Napi::TypeError::New(env, ErrMsg::Class::ModulePassManager::addFunctionPasses); + } + + passManager->addPass(llvm::createModuleToFunctionPassAdaptor(std::move(*fpm))); +} + +Napi::Value ModulePassManager::isEmpty(const Napi::CallbackInfo &info) { + return Napi::Boolean::New(info.Env(), passManager->isEmpty()); +} + +void ModulePassManager::run(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + auto argsLen = info.Length(); + + if (argsLen != 1 || !Module::IsClassOf(info[0])) { + throw Napi::TypeError::New(env, ErrMsg::Class::ModulePassManager::run); + } + + auto module = Module::Extract(info[0]); + + if (!module) { + throw Napi::TypeError::New(env, ErrMsg::Class::ModulePassManager::run); + } + + passManager->run(*module, *moduleAnalysisManager); +} + +//===----------------------------------------------------------------------===// +// FunctionPassManager Class +//===----------------------------------------------------------------------===// + +void FunctionPassManager::Init(Napi::Env env, Napi::Object &exports) { + Napi::HandleScope scope(env); + Napi::Function func = DefineClass(env, "FunctionPassManager", { + InstanceMethod("addSROAPass", &FunctionPassManager::addSROAPass), + InstanceMethod("addEarlyCSEPass", &FunctionPassManager::addEarlyCSEPass), + InstanceMethod("addInstCombinePass", &FunctionPassManager::addInstCombinePass), + InstanceMethod("isEmpty", &FunctionPassManager::isEmpty), + }); + constructor = Napi::Persistent(func); + constructor.SuppressDestruct(); + Inherit(env, constructor.Value(), GlobalObject::constructor.Value()); + exports.Set("FunctionPassManager", func); +} + +Napi::Object FunctionPassManager::New(Napi::Env env, llvm::FunctionPassManager *fpm) { + return constructor.New({Napi::External::New(env, fpm)}); +} + +bool FunctionPassManager::IsClassOf(const Napi::Value &value) { + return value.IsNull() || value.As().InstanceOf(constructor.Value()); +} + +llvm::FunctionPassManager *FunctionPassManager::Extract(const Napi::Value &value) { + if (value.IsNull()) { + return nullptr; + } + return Unwrap(value.As())->getLLVMPrimitive(); +} + +FunctionPassManager::FunctionPassManager(const Napi::CallbackInfo &info) : ObjectWrap(info) { + Napi::Env env = info.Env(); + if (info.IsConstructCall() && info.Length() == 1 && info[0].IsExternal()) { + auto external = info[0].As>(); + passManager = external.Data(); + return; + } + throw Napi::TypeError::New(env, ErrMsg::Class::FunctionPassManager::constructor); +} + +llvm::FunctionPassManager *FunctionPassManager::getLLVMPrimitive() { + return passManager; +} + +void FunctionPassManager::addSROAPass(const Napi::CallbackInfo &info) { + passManager->addPass(llvm::SROAPass()); +} + +void FunctionPassManager::addEarlyCSEPass(const Napi::CallbackInfo &info) { + bool useSSA = false; + + if (info.Length() >= 1 && info[0].IsBoolean()) { + useSSA = info[0].As(); + } + + passManager->addPass(llvm::EarlyCSEPass(useSSA)); +} + +void FunctionPassManager::addInstCombinePass(const Napi::CallbackInfo& info) { + passManager->addPass(llvm::InstCombinePass()); +} + +Napi::Value FunctionPassManager::isEmpty(const Napi::CallbackInfo &info) { + return Napi::Boolean::New(info.Env(), passManager->isEmpty()); +} diff --git a/src/IR/index.cpp b/src/IR/index.cpp index 0428651..7d31977 100644 --- a/src/IR/index.cpp +++ b/src/IR/index.cpp @@ -23,12 +23,14 @@ void InitIR(Napi::Env env, Napi::Object &exports) { ConstantPointerNull::Init(env, exports); ConstantDataArray::Init(env, exports); ConstantExpr::Init(env, exports); + ConstantArray::Init(env, exports); UndefValue::Init(env, exports); GlobalValue::Init(env, exports); GlobalObject::Init(env, exports); GlobalVariable::Init(env, exports); Attribute::Init(env, exports); Function::Init(env, exports); + CallingConv::Init(env, exports); Instruction::Init(env, exports); AllocaInst::Init(env, exports); LoadInst::Init(env, exports); @@ -105,4 +107,6 @@ void InitIR(Napi::Env env, Napi::Object &exports) { DataLayout::Init(env, exports); Verifier::Init(env, exports); Intrinsic::Init(env, exports); + ModulePassManager::Init(env, exports); + FunctionPassManager::Init(env, exports); } diff --git a/src/MC/TargetRegistry.cpp b/src/MC/TargetRegistry.cpp index 87d9ca7..d3ba134 100644 --- a/src/MC/TargetRegistry.cpp +++ b/src/MC/TargetRegistry.cpp @@ -32,20 +32,100 @@ Target::Target(const Napi::CallbackInfo &info) : Napi::ObjectWrap{info} throw Napi::TypeError::New(env, ErrMsg::Class::Target::constructor); } +bool isCreateTargetDefaultArgs(const Napi::CallbackInfo &info, unsigned argsLen) { + return argsLen == 2 && info[0].IsString() && info[1].IsString() + || argsLen == 2 && info[0].IsString() && info[1].IsString() && info[2].IsString(); +} + +bool isCreateTargetRelocArgs(const Napi::CallbackInfo &info, unsigned argsLen) { + return argsLen == 4 && info[0].IsString() && info[1].IsString() && info[2].IsString() && info[3].IsNumber(); +} + +bool isCreateTargetCodeModelArgs(const Napi::CallbackInfo &info, unsigned argsLen) { + return argsLen == 5 + && info[0].IsString() + && info[1].IsString() + && info[2].IsString() + && info[3].IsNumber() + && info[4].IsNumber(); +} + +bool isCreateTargetCodeGenOptArgs(const Napi::CallbackInfo &info, unsigned argsLen) { + return argsLen == 6 + && info[0].IsString() + && info[1].IsString() + && info[2].IsString() + && info[3].IsNumber() + && info[4].IsNumber() + && info[5].IsNumber(); +} + +bool isCreateTargetJitArgs(const Napi::CallbackInfo &info, unsigned argsLen) { + return argsLen == 7 + && info[0].IsString() + && info[1].IsString() + && info[2].IsString() + && info[3].IsNumber() + && info[4].IsNumber() + && info[5].IsNumber() + && info[6].IsBoolean(); +} + Napi::Value Target::createTargetMachine(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); unsigned argsLen = info.Length(); - if (argsLen < 2 || !info[0].IsString() || !info[1].IsString() || argsLen >= 3 && !info[2].IsString()) { + + auto isCreateTargetDefault = isCreateTargetDefaultArgs(info, argsLen); + auto isCreateTargetReloc = isCreateTargetRelocArgs(info, argsLen); + auto isCreateTargetCodeModel = isCreateTargetCodeModelArgs(info, argsLen); + auto isCreateTargetCodeGenOpt = isCreateTargetCodeGenOptArgs(info, argsLen); + auto isCreateTargetJit = isCreateTargetJitArgs(info, argsLen); + + auto isValidCall = isCreateTargetDefault + || isCreateTargetReloc + || isCreateTargetCodeModel + || isCreateTargetCodeGenOpt + || isCreateTargetJit; + + if (!isValidCall) { throw Napi::TypeError::New(env, ErrMsg::Class::Target::createTargetMachine); } + std::string targetTriple = info[0].As(); std::string cpu = info[1].As(); std::string features; + if (argsLen >= 3) { features = info[2].As(); } + + if (isCreateTargetReloc) { + llvm::Optional relocModel = (llvm::Reloc::Model) info[3].As().Uint32Value(); + llvm::TargetOptions options{}; + llvm::TargetMachine *targetMachinePtr = target->createTargetMachine(targetTriple, cpu, features, options, relocModel); + return TargetMachine::New(env, targetMachinePtr); + } + + if (isCreateTargetCodeModel) { + llvm::Optional relocModel = (llvm::Reloc::Model) info[3].As().Uint32Value(); + llvm::Optional codeModel = (llvm::CodeModel::Model) info[4].As().Uint32Value(); + llvm::TargetOptions options{}; + llvm::TargetMachine *targetMachinePtr = target->createTargetMachine(targetTriple, cpu, features, options, relocModel, codeModel); + return TargetMachine::New(env, targetMachinePtr); + } + + if (isCreateTargetCodeGenOpt) { + llvm::Optional relocModel = (llvm::Reloc::Model) info[3].As().Uint32Value(); + llvm::Optional codeModel = (llvm::CodeModel::Model) info[4].As().Uint32Value(); + auto codeGenOpt = (llvm::CodeGenOpt::Level) info[4].As().Uint32Value(); + llvm::TargetOptions options{}; + llvm::TargetMachine *targetMachinePtr = target->createTargetMachine(targetTriple, cpu, features, options, relocModel, codeModel, codeGenOpt); + return TargetMachine::New(env, targetMachinePtr); + } + + llvm::Optional relocModel{}; llvm::TargetOptions options{}; - llvm::TargetMachine *targetMachinePtr = target->createTargetMachine(targetTriple, cpu, features, options, llvm::Optional{}); + llvm::TargetMachine *targetMachinePtr = target->createTargetMachine(targetTriple, cpu, features, options, relocModel); return TargetMachine::New(env, targetMachinePtr); } diff --git a/src/Passes/OptimizationLevel.cpp b/src/Passes/OptimizationLevel.cpp new file mode 100644 index 0000000..9257742 --- /dev/null +++ b/src/Passes/OptimizationLevel.cpp @@ -0,0 +1,12 @@ +#include "Passes/index.h" + +void OptimizationLevel::Init(Napi::Env env, Napi::Object &exports) { + Napi::Object optimizationLevelNS = Napi::Object::New(env); + optimizationLevelNS.Set("O0", Napi::Number::New(env, Level::O0)); + optimizationLevelNS.Set("O1", Napi::Number::New(env, Level::O1)); + optimizationLevelNS.Set("O2", Napi::Number::New(env, Level::O2)); + optimizationLevelNS.Set("O3", Napi::Number::New(env, Level::O3)); + optimizationLevelNS.Set("Os", Napi::Number::New(env, Level::Os)); + optimizationLevelNS.Set("Oz", Napi::Number::New(env, Level::Oz)); + exports.Set("OptimizationLevel", optimizationLevelNS); +} diff --git a/src/Passes/ThinOrFullLTOPhase.cpp b/src/Passes/ThinOrFullLTOPhase.cpp new file mode 100644 index 0000000..6dafeb6 --- /dev/null +++ b/src/Passes/ThinOrFullLTOPhase.cpp @@ -0,0 +1,13 @@ +#include "Passes/index.h" + +#include "llvm/Pass.h" + +void ThinOrFullLTOPhase::Init(Napi::Env env, Napi::Object &exports) { + Napi::Object ltoPhaseNS = Napi::Object::New(env); + ltoPhaseNS.Set("None", Napi::Number::New(env, (unsigned) llvm::ThinOrFullLTOPhase::None)); + ltoPhaseNS.Set("ThinLTOPreLink", Napi::Number::New(env, (unsigned) llvm::ThinOrFullLTOPhase::ThinLTOPreLink)); + ltoPhaseNS.Set("ThinLTOPostLink", Napi::Number::New(env, (unsigned) llvm::ThinOrFullLTOPhase::ThinLTOPostLink)); + ltoPhaseNS.Set("FullLTOPreLink", Napi::Number::New(env, (unsigned) llvm::ThinOrFullLTOPhase::FullLTOPreLink)); + ltoPhaseNS.Set("FullLTOPostLink", Napi::Number::New(env, (unsigned) llvm::ThinOrFullLTOPhase::FullLTOPostLink)); + exports.Set("ThinOrFullLTOPhase", ltoPhaseNS); +} diff --git a/src/Passes/index.cpp b/src/Passes/index.cpp new file mode 100644 index 0000000..6ce5e1a --- /dev/null +++ b/src/Passes/index.cpp @@ -0,0 +1,6 @@ +#include "Passes/index.h" + +void InitPasses(Napi::Env env, Napi::Object &exports) { + OptimizationLevel::Init(env, exports); + ThinOrFullLTOPhase::Init(env, exports); +} diff --git a/src/Support/CodeGen.cpp b/src/Support/CodeGen.cpp new file mode 100644 index 0000000..be7ce9a --- /dev/null +++ b/src/Support/CodeGen.cpp @@ -0,0 +1,86 @@ +#include "Support/index.h" + +#include + +#define SET_ENUM_VALUE_NUM(obj, en, name) \ + obj.Set(#name, Napi::Number::New(env, en::name)) + +#define SET_NAME_ENUM_VALUE_NUM(obj, prop, en, name) \ + obj.Set(#prop, Napi::Number::New(env, en::name)) + +void initReloc(Napi::Env env, Napi::Object &exports) { + Napi::Object relocNS = Napi::Object::New(env); + SET_ENUM_VALUE_NUM(relocNS, llvm::Reloc, Static); + SET_ENUM_VALUE_NUM(relocNS, llvm::Reloc, Static); + SET_ENUM_VALUE_NUM(relocNS, llvm::Reloc, PIC_); + SET_ENUM_VALUE_NUM(relocNS, llvm::Reloc, DynamicNoPIC); + SET_ENUM_VALUE_NUM(relocNS, llvm::Reloc, ROPI); + SET_ENUM_VALUE_NUM(relocNS, llvm::Reloc, RWPI); + SET_ENUM_VALUE_NUM(relocNS, llvm::Reloc, ROPI_RWPI); + exports.Set("Reloc", relocNS); +} + +void initCodeModel(Napi::Env env, Napi::Object &exports) { + Napi::Object codeModelNS = Napi::Object::New(env); + SET_ENUM_VALUE_NUM(codeModelNS, llvm::CodeModel, Tiny); + SET_ENUM_VALUE_NUM(codeModelNS, llvm::CodeModel, Small); + SET_ENUM_VALUE_NUM(codeModelNS, llvm::CodeModel, Kernel); + SET_ENUM_VALUE_NUM(codeModelNS, llvm::CodeModel, Medium); + SET_ENUM_VALUE_NUM(codeModelNS, llvm::CodeModel, Large); + exports.Set("CodeModel", codeModelNS); +} + +void initPICLevel(Napi::Env env, Napi::Object &exports) { + Napi::Object picLevelNS = Napi::Object::New(env); + SET_ENUM_VALUE_NUM(picLevelNS, llvm::PICLevel, NotPIC); + SET_ENUM_VALUE_NUM(picLevelNS, llvm::PICLevel, SmallPIC); + SET_ENUM_VALUE_NUM(picLevelNS, llvm::PICLevel, BigPIC); + exports.Set("PICLevel", picLevelNS); +} + +void initPIELevel(Napi::Env env, Napi::Object &exports) { + Napi::Object pieLevelNS = Napi::Object::New(env); + SET_ENUM_VALUE_NUM(pieLevelNS, llvm::PIELevel, Default); + SET_ENUM_VALUE_NUM(pieLevelNS, llvm::PIELevel, Small); + SET_ENUM_VALUE_NUM(pieLevelNS, llvm::PIELevel, Large); + exports.Set("PIELevel", pieLevelNS); +} + +void initTLSModel(Napi::Env env, Napi::Object &exports) { + Napi::Object tlsModelNS = Napi::Object::New(env); + SET_ENUM_VALUE_NUM(tlsModelNS, llvm::TLSModel, GeneralDynamic); + SET_ENUM_VALUE_NUM(tlsModelNS, llvm::TLSModel, LocalDynamic); + SET_ENUM_VALUE_NUM(tlsModelNS, llvm::TLSModel, InitialExec); + SET_ENUM_VALUE_NUM(tlsModelNS, llvm::TLSModel, LocalExec); + exports.Set("TLSModel", tlsModelNS); +} + +void initCodeGenOpt(Napi::Env env, Napi::Object &exports) { + Napi::Object codeGenOptNS = Napi::Object::New(env); + SET_ENUM_VALUE_NUM(codeGenOptNS, llvm::CodeGenOpt, None); + SET_ENUM_VALUE_NUM(codeGenOptNS, llvm::CodeGenOpt, Less); + SET_ENUM_VALUE_NUM(codeGenOptNS, llvm::CodeGenOpt, Default); + SET_ENUM_VALUE_NUM(codeGenOptNS, llvm::CodeGenOpt, Aggressive); + exports.Set("CodeGenOpt", codeGenOptNS); +} + +void initCodeGenFileType(Napi::Env env, Napi::Object &exports) { + Napi::Object codeGenFileTypeNS = Napi::Object::New(env); + SET_NAME_ENUM_VALUE_NUM(codeGenFileTypeNS, Assembly, llvm::CodeGenFileType, CGFT_AssemblyFile); + SET_NAME_ENUM_VALUE_NUM(codeGenFileTypeNS, Object, llvm::CodeGenFileType, CGFT_ObjectFile); + SET_NAME_ENUM_VALUE_NUM(codeGenFileTypeNS, Null, llvm::CodeGenFileType, CGFT_Null); + exports.Set("CodeGenFileType", codeGenFileTypeNS); +} + +void CodeGen::Init(Napi::Env env, Napi::Object &exports) { + initReloc(env, exports); + initCodeModel(env, exports); + initPICLevel(env, exports); + initPIELevel(env, exports); + initTLSModel(env, exports); + initCodeGenOpt(env, exports); + initCodeGenFileType(env, exports); +} + +#undef SET_ENUM_VALUE_NUM +#undef SET_NAME_ENUM_VALUE_NUM diff --git a/src/Support/index.cpp b/src/Support/index.cpp index 4f934dd..edc02ba 100644 --- a/src/Support/index.cpp +++ b/src/Support/index.cpp @@ -3,4 +3,5 @@ void InitSupport(Napi::Env env, Napi::Object &exports) { SMDiagnostic::Init(env, exports); InitTargetSelect(env, exports); + CodeGen::Init(env, exports); } diff --git a/src/Target/TargetMachine.cpp b/src/Target/TargetMachine.cpp index d6baabe..d5a7b86 100644 --- a/src/Target/TargetMachine.cpp +++ b/src/Target/TargetMachine.cpp @@ -2,10 +2,18 @@ #include "IR/index.h" #include "Util/index.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/LegacyPassManager.h" + +#include + void TargetMachine::Init(Napi::Env env, Napi::Object &exports) { Napi::HandleScope scope(env); Napi::Function func = DefineClass(env, "TargetMachine", { - InstanceMethod("createDataLayout", &TargetMachine::createDataLayout) + InstanceMethod("createDataLayout", &TargetMachine::createDataLayout), + InstanceMethod("emitToFile", &TargetMachine::emitToFile), + InstanceMethod("emitToBuffer", &TargetMachine::emitToBuffer) }); constructor = Napi::Persistent(func); constructor.SuppressDestruct(); @@ -35,3 +43,53 @@ Napi::Value TargetMachine::createDataLayout(const Napi::CallbackInfo &info) { const llvm::DataLayout &dataLayout = targetMachine->createDataLayout(); return DataLayout::New(env, const_cast(&dataLayout)); } + +void TargetMachine::emitToFile(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + unsigned argsLen = info.Length(); + + if (argsLen != 3 || !Module::IsClassOf(info[0]) || !info[1].IsString() || !info[2].IsNumber()) { + throw Napi::TypeError::New(env, ErrMsg::Class::TargetMachine::emitToFile); + } + + auto fileName = info[1].As().Utf8Value(); + std::error_code ec; + llvm::raw_fd_ostream dest(fileName, ec, llvm::sys::fs::OF_None); + + auto module = Module::Extract(info[0]); + auto type = (llvm::CodeGenFileType) info[2].As().Uint32Value(); + + llvm::legacy::PassManager pm{}; + + if (const_cast(targetMachine)->addPassesToEmitFile(pm, dest, nullptr, type)) { + throw Napi::Error::New(env, ErrMsg::Class::TargetMachine::addPassesToEmitFile); + } + + pm.run(*module); + dest.flush(); +} + +Napi::Value TargetMachine::emitToBuffer(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + unsigned argsLen = info.Length(); + + if (argsLen != 2 || !Module::IsClassOf(info[0]) || !info[1].IsNumber()) { + throw Napi::TypeError::New(env, ErrMsg::Class::TargetMachine::emitToBuffer); + } + + llvm::SmallVector buffer; + llvm::raw_svector_ostream dest(buffer); + + auto module = Module::Extract(info[0]); + auto type = (llvm::CodeGenFileType) info[1].As().Uint32Value(); + + llvm::legacy::PassManager pm{}; + + if (const_cast(targetMachine)->addPassesToEmitFile(pm, dest, nullptr, type)) { + throw Napi::Error::New(env, ErrMsg::Class::TargetMachine::addPassesToEmitFile); + } + + pm.run(*module); + + return Napi::Value(env, Napi::Buffer::New(env, buffer.data(), buffer.size())); +} diff --git a/src/llvm-bindings.cpp b/src/llvm-bindings.cpp index ff01048..c89468e 100644 --- a/src/llvm-bindings.cpp +++ b/src/llvm-bindings.cpp @@ -8,6 +8,7 @@ #include "MC/index.h" #include "Support/index.h" #include "Target/index.h" +#include "Passes/index.h" Napi::Object Init(Napi::Env env, Napi::Object exports) { InitADT(env, exports); @@ -20,6 +21,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) { InitMC(env, exports); InitSupport(env, exports); InitTarget(env, exports); + InitPasses(env, exports); return exports; } diff --git a/tests/Target/TargetMachine.spec.ts b/tests/Target/TargetMachine.spec.ts index d261fef..bd78ffc 100644 --- a/tests/Target/TargetMachine.spec.ts +++ b/tests/Target/TargetMachine.spec.ts @@ -1,7 +1,22 @@ +import fs from 'fs'; +import path from 'path'; + import llvm from '../..'; +import { createTestModule } from "./testModule"; + +const TARGET_TRIPLE = 'x86_64-unknown-unknown'; +const TARGET_CPU = 'generic'; +const TARGET_FEATURES = ''; +const TARGET_GEN_OPT_LEVEL = llvm.CodeGenOpt.None +const TARGET_OPT_LEVEL = llvm.OptimizationLevel.O0; +const TARGET_RELOC_TYPE = llvm.Reloc.Static; +const TARGET_CODE_MODEL = llvm.CodeModel.Medium; +const TARGET_FORMAT = llvm.CodeGenFileType.Object; describe('Test TargetMachine', () => { beforeAll(() => { + llvm.InitializeAllAsmPrinters(); + llvm.InitializeAllAsmParsers(); llvm.InitializeAllTargetInfos(); llvm.InitializeAllTargets(); llvm.InitializeAllTargetMCs(); @@ -11,9 +26,73 @@ describe('Test TargetMachine', () => { const target = llvm.TargetRegistry.lookupTarget('x86_64'); expect(target).toBeInstanceOf(llvm.Target); if (target) { - const machine = target.createTargetMachine('x86_64-unknown-unknown', 'generic'); + const machine = target.createTargetMachine(TARGET_TRIPLE, TARGET_CPU); const dataLayout = machine.createDataLayout(); expect(dataLayout).toBeInstanceOf(llvm.DataLayout); } }); + + test('Test llvm.TargetMachine.emitToFile', () => { + const target = llvm.TargetRegistry.lookupTarget(TARGET_TRIPLE); + + if (!target) { + return; + } + + const targetMachine = target.createTargetMachine( + TARGET_TRIPLE, + TARGET_CPU, + TARGET_FEATURES, + TARGET_RELOC_TYPE, + TARGET_CODE_MODEL, + TARGET_GEN_OPT_LEVEL, + ); + + const mpm = new llvm.ModulePassManager(TARGET_OPT_LEVEL); + const fpm = mpm.createFunctionPassManager(llvm.ThinOrFullLTOPhase.None); + + const module = createTestModule(); + const filePath = path.resolve(__dirname, 'target-machine.test.o'); + + mpm.addVerifierPass(); + mpm.addFunctionPasses(fpm); + mpm.run(module); + + targetMachine.emitToFile(module, filePath, TARGET_FORMAT); + expect(fs.existsSync(filePath)).toBe(true); + expect(fs.statSync(filePath)?.size).toBe(656); + + try { + fs.unlinkSync(filePath); + } finally {} + }); + + test('Test llvm.TargetMachine.emitToBuffer', () => { + const target = llvm.TargetRegistry.lookupTarget(TARGET_TRIPLE); + + if (!target) { + return; + } + + const targetMachine = target.createTargetMachine( + TARGET_TRIPLE, + TARGET_CPU, + TARGET_FEATURES, + TARGET_RELOC_TYPE, + TARGET_CODE_MODEL, + TARGET_GEN_OPT_LEVEL, + ); + + const mpm = new llvm.ModulePassManager(TARGET_OPT_LEVEL); + const fpm = mpm.createFunctionPassManager(llvm.ThinOrFullLTOPhase.None); + + const module = createTestModule(); + + mpm.addVerifierPass(); + mpm.addFunctionPasses(fpm); + mpm.run(module); + + const data = targetMachine.emitToBuffer(module, TARGET_FORMAT); + expect(data.length).toBe(656); + }); }); diff --git a/tests/Target/testModule.ts b/tests/Target/testModule.ts new file mode 100644 index 0000000..a3d98aa --- /dev/null +++ b/tests/Target/testModule.ts @@ -0,0 +1,33 @@ +import llvm from "../.."; + +export const createTestModule = () => { + const context = new llvm.LLVMContext(); + const module = new llvm.Module('test', context); + const builder = new llvm.IRBuilder(context); + + const returnType = builder.getInt32Ty(); + const paramTypes = [builder.getInt32Ty()]; + const functionType = llvm.FunctionType.get(returnType, paramTypes, false); + const func = llvm.Function.Create(functionType, llvm.Function.LinkageTypes.ExternalLinkage, 'fibonacci', module); + + const entryBB = llvm.BasicBlock.Create(context, 'entry', func); + const thenBB = llvm.BasicBlock.Create(context, 'then', func); + const elseBB = llvm.BasicBlock.Create(context, 'else', func); + + builder.SetInsertPoint(entryBB); + const cond = builder.CreateICmpULE(func.getArg(0), builder.getInt32(1), 'cond'); + builder.CreateCondBr(cond, thenBB, elseBB); + + builder.SetInsertPoint(thenBB); + builder.CreateRet(func.getArg(0)); + + builder.SetInsertPoint(elseBB); + const n_1 = builder.CreateSub(func.getArg(0), builder.getInt32(1), 'n_1'); + const n_2 = builder.CreateSub(func.getArg(0), builder.getInt32(2), 'n_2'); + const ret1 = builder.CreateCall(func, [n_1], 'ret1'); + const ret2 = builder.CreateCall(func, [n_2], 'ret2'); + const result = builder.CreateAdd(ret1, ret2); + builder.CreateRet(result); + + return module; +};