irgenerator/ir-generator.spice
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | // Std imports | ||
| 4 | import "std/type/any"; | ||
| 5 | import "std/data/vector"; | ||
| 6 | import "std/data/stack"; | ||
| 7 | import "std/data/unordered-map"; | ||
| 8 | import "std/data/pair"; | ||
| 9 | import "std/text/stringstream"; | ||
| 10 | import "std/type/type-conversion"; | ||
| 11 | import "std/type/types"; | ||
| 12 | import "std/bindings/llvm/llvm" as llvm; | ||
| 13 | |||
| 14 | // Own imports | ||
| 15 | import "@/exception/fatal-error"; | ||
| 16 | import "@/compiler-pass"; | ||
| 17 | import "@/driver"; | ||
| 18 | import "@/source-file"; | ||
| 19 | import "@/ast/ast-nodes"; | ||
| 20 | import "@/ast/attributes"; | ||
| 21 | import "@/ast/abstract-ast-visitor-intf"; | ||
| 22 | import "@/exception/compiler-error"; | ||
| 23 | import "@/exception/semantic-error"; | ||
| 24 | import "@/global/global-resource-manager"; | ||
| 25 | import "@/global/runtime-module-manager"; | ||
| 26 | import "@/irgenerator/abi-info"; | ||
| 27 | import "@/irgenerator/debug-info-generator"; | ||
| 28 | import "@/irgenerator/llvm-expr-result"; | ||
| 29 | import "@/irgenerator/metadata-generator"; | ||
| 30 | import "@/irgenerator/name-mangling"; | ||
| 31 | import "@/irgenerator/op-rule-conversion-manager"; | ||
| 32 | import "@/irgenerator/std-function-manager"; | ||
| 33 | import "@/model/function" as model; // Aliased, because Function clashes with llvm::Function | ||
| 34 | import "@/model/generic-type"; | ||
| 35 | import "@/model/interface"; | ||
| 36 | import "@/model/struct"; | ||
| 37 | import "@/model/struct-base"; | ||
| 38 | import "@/reader/code-loc"; | ||
| 39 | import "@/symboltablebuilder/capture"; | ||
| 40 | import "@/symboltablebuilder/qual-type"; | ||
| 41 | import "@/symboltablebuilder/scope"; | ||
| 42 | import "@/symboltablebuilder/scope-handle"; | ||
| 43 | import "@/symboltablebuilder/super-type"; | ||
| 44 | import "@/symboltablebuilder/symbol-table"; | ||
| 45 | import "@/symboltablebuilder/symbol-table-entry"; | ||
| 46 | import "@/symboltablebuilder/type-chain"; | ||
| 47 | import "@/symboltablebuilder/type-qualifiers"; | ||
| 48 | import "@/typechecker/builtin-functions"; | ||
| 49 | import "@/typechecker/function-manager"; | ||
| 50 | |||
| 51 | // Constants | ||
| 52 | public const string ANON_GLOBAL_STRING_NAME = "anon.string."; | ||
| 53 | public const string ANON_GLOBAL_ARRAY_NAME = "anon.array."; | ||
| 54 | public const string CAPTURES_PARAM_NAME = "captures"; | ||
| 55 | const int EXIT_FAILURE = 1; | ||
| 56 | |||
| 57 | // String placeholders for builtin testing output | ||
| 58 | const string TEST_ALL_START_MSG = "[==========] Running %d test(s) from %d source file(s)\n"; | ||
| 59 | const string TEST_ALL_END_MSG = "[==========] Ran %d test(s) from %d source file(s)\n"; | ||
| 60 | const string TEST_FILE_START_MSG = "[----------] Running %d test(s) from %s\n"; | ||
| 61 | const string TEST_FILE_END_MSG = "[----------] Ran %d test(s) from %s\n\n"; | ||
| 62 | const string TEST_CASE_RUN_MSG = "[ RUN ] %s\n"; | ||
| 63 | const string TEST_CASE_SUCCESS_MSG = "\033[1m\033[32m[ PASSED ]\033[0m\033[22m %s\n"; | ||
| 64 | const string TEST_CASE_FAILED_MSG = "\033[1m\033[31m[ FAILED ]\033[0m\033[22m %s\n"; | ||
| 65 | const string TEST_CASE_SKIPPED_MSG = "\033[1m\033[33m[ SKIPPED ]\033[0m\033[22m %s\n"; | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Get the producer string, that is attached to the generated modules | ||
| 69 | * | ||
| 70 | * @return Producer string | ||
| 71 | */ | ||
| 72 | 3622 | public f<String> getProducerString() { | |
| 73 | 3622 | const string version = __get_build_var<string>("SPICE_VERSION", "dev"); | |
| 74 | 7244 | return String("spice version ") + version + " (https://github.com/spicelang/spice)"; | |
| 75 | 3622 | } | |
| 76 | |||
| 77 | public type Likelihood enum { | ||
| 78 | UNSPECIFIED, | ||
| 79 | LIKELY, | ||
| 80 | UNLIKELY | ||
| 81 | } | ||
| 82 | |||
| 83 | 3499 | public type CommonLLVMTypes struct { | |
| 84 | public llvm::Type lambdaFatPtrType | ||
| 85 | } | ||
| 86 | |||
| 87 | // A break/continue target: the scope to run cleanup (dtor calls) up to (inclusive) before jumping, since the | ||
| 88 | // jump skips the normal fall-through cleanup of enclosing scopes, paired with the block to jump to. | ||
| 89 | public type BreakContinueTarget struct { | ||
| 90 | public Scope* scope | ||
| 91 | public llvm::BasicBlock block | ||
| 92 | } | ||
| 93 | |||
| 94 | // Name and symbol table entry of a parameter | ||
| 95 | 226113 | public type ParamInfo struct { | |
| 96 | public String name | ||
| 97 | public const SymbolTableEntry* entry | ||
| 98 | } | ||
| 99 | |||
| 100 | public type ParamInfoList alias Vector<ParamInfo>; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Generates the LLVM IR of a source file. | ||
| 104 | * | ||
| 105 | * Spice requires all methods of a struct to live in the file of the struct, so this file is split into sections, that are | ||
| 106 | * named after the generated constructs (top level definitions, statements, expressions, ...). | ||
| 107 | * | ||
| 108 | * Notes: | ||
| 109 | * - The LLVM C API offers no way to set the dso_local flag of global values, so the generated IR has no dso_local markers. | ||
| 110 | * This does not change the generated code, since the target machine assumes dso_local for all symbols of the executable. | ||
| 111 | * - Implicit functions and procedures are generated by a prologue and an epilogue, that surround the body generation at the | ||
| 112 | * call site. | ||
| 113 | * - If an instruction takes two operands, which both emit IR (e.g. the lhs and rhs of an addition), the rhs is emitted first. | ||
| 114 | */ | ||
| 115 | 3499 | public type IRGenerator struct : IAbstractAstVisitor { | |
| 116 | compose public CompilerPass compilerPass | ||
| 117 | public llvm::LLVMContext* context | ||
| 118 | public llvm::IRBuilder* builder | ||
| 119 | public llvm::Module* module | ||
| 120 | ABIInfo abiInfo | ||
| 121 | public OpRuleConversionManager conversionManager | ||
| 122 | public StdFunctionManager stdFunctionManager | ||
| 123 | public DebugInfoGenerator diGenerator | ||
| 124 | public MetadataGenerator mdGenerator | ||
| 125 | public CommonLLVMTypes llvmTypes | ||
| 126 | Vector<BreakContinueTarget> breakTargets | ||
| 127 | Vector<BreakContinueTarget> continueTargets | ||
| 128 | Stack<llvm::BasicBlock> fallthroughBlocks | ||
| 129 | llvm::BasicBlock allocaInsertBlock | ||
| 130 | llvm::Value allocaInsertInst | ||
| 131 | 3499 | bool blockAlreadyTerminated = false | |
| 132 | 3499 | bool isInCtorBody = false | |
| 133 | Vector<const StructBase*> deferredVTableInitializations | ||
| 134 | // IR-side state: separate from semantic objects to keep the type-checker model clean. Both maps are keyed by the address of | ||
| 135 | // the symbol table entry / function, since pointers cannot be hashed. | ||
| 136 | UnorderedMap<long, Stack<llvm::Value>> addressMap | ||
| 137 | UnorderedMap<long, llvm::Function> llvmFunctions | ||
| 138 | } | ||
| 139 | |||
| 140 | 3499 | public p IRGenerator.ctor(GlobalResourceManager& resourceManager, SourceFile* sourceFile) { | |
| 141 | 3499 | this.compilerPass = CompilerPass(resourceManager, sourceFile); | |
| 142 | 3499 | llvm::LLVMContext& llvmContext = sourceFile.getLLVMContext(); | |
| 143 | 3499 | this.context = &llvmContext; | |
| 144 | 3499 | this.builder = &sourceFile.builder; | |
| 145 | 3499 | this.module = &sourceFile.llvmModule; | |
| 146 | 3499 | this.abiInfo = ABIInfo(this.cliOptions.targetTriple, this.module, this.context); | |
| 147 | 3499 | this.stdFunctionManager = StdFunctionManager(sourceFile, this.module, this); | |
| 148 | 3499 | this.conversionManager = OpRuleConversionManager(this); | |
| 149 | 3499 | this.diGenerator = DebugInfoGenerator(this); | |
| 150 | 3499 | this.mdGenerator = MetadataGenerator(this); | |
| 151 | |||
| 152 | // Attach information to the module | ||
| 153 | 3499 | this.module.setTargetTriple(this.cliOptions.targetTriple); | |
| 154 | 3499 | this.module.setDataLayout(sourceFile.targetMachine.createDataLayout()); | |
| 155 | 3499 | if this.cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY { | |
| 156 | 1 | this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MIN, "PIC Level", 1); // llvm::PICLevel::SmallPIC | |
| 157 | 1 | this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "PIE Level", 0); // llvm::PIELevel::Default | |
| 158 | 1 | } else { | |
| 159 | 3498 | this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MIN, "PIC Level", 2); // llvm::PICLevel::BigPIC | |
| 160 | 3498 | this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "PIE Level", 2); // llvm::PIELevel::Large | |
| 161 | 3498 | } | |
| 162 | 3499 | this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "uwtable", 2); // llvm::UWTableKind::Default | |
| 163 | // llvm::FramePointerKind::All or llvm::FramePointerKind::None | ||
| 164 | 3499 | this.module.addModuleFlag(llvm::MOD_FLAG_BEHAVIOR_MAX, "frame-pointer", this.cliOptions.keepFramePointers ? 2 : 0); | |
| 165 | |||
| 166 | // Add module identifier metadata | ||
| 167 | 3499 | this.module.addNamedStringMetadata("llvm.ident", getProducerString()); | |
| 168 | |||
| 169 | // Initialize common LLVM types | ||
| 170 | 3499 | Vector<llvm::Type> fatPtrFieldTypes; | |
| 171 | 3499 | fatPtrFieldTypes.pushBack(this.builder.getPtrTy()); | |
| 172 | 3499 | fatPtrFieldTypes.pushBack(this.builder.getPtrTy()); | |
| 173 | 3499 | fatPtrFieldTypes.pushBack(this.builder.getInt64Ty()); | |
| 174 | 3499 | this.llvmTypes.lambdaFatPtrType = llvm::getStructType(*this.context, fatPtrFieldTypes); | |
| 175 | |||
| 176 | // Initialize debug info generator | ||
| 177 | 3499 | if this.cliOptions.instrumentation.emitsDebugInfo() { | |
| 178 | 123 | this.diGenerator.initialize(sourceFile.fileName, sourceFile.fileDir); | |
| 179 | 123 | } | |
| 180 | 3499 | } | |
| 181 | |||
| 182 | // ============================================================= Visit =========================================================== | ||
| 183 | |||
| 184 | // Visit a node, which evaluates to an expression result | ||
| 185 | 552915 | f<LLVMExprResult> IRGenerator.visitExpr(IVisitable* node) { | |
| 186 | 552915 | Any visitResult = this.visit(node); | |
| 187 | 1105830 | return visitResult.get<LLVMExprResult>(); | |
| 188 | 552915 | } | |
| 189 | |||
| 190 | // Visit a node, which evaluates to a constant (e.g. a constant node) | ||
| 191 | 78074 | f<llvm::Value> IRGenerator.visitConst(IVisitable* node) { | |
| 192 | 78074 | Any visitResult = this.visit(node); | |
| 193 | 156148 | return visitResult.get<llvm::Value>(); | |
| 194 | 78074 | } | |
| 195 | |||
| 196 | // Visit a node, which evaluates to a type (e.g. a data type node) | ||
| 197 | 10048 | f<llvm::Type> IRGenerator.visitType(IVisitable* node) { | |
| 198 | 10048 | Any visitResult = this.visit(node); | |
| 199 | 20096 | return visitResult.get<llvm::Type>(); | |
| 200 | 10048 | } | |
| 201 | |||
| 202 | // Return value of a visit method, that evaluates to an expression result | ||
| 203 | 615342 | f<Any> exprResultAny(const LLVMExprResult& exprResult) { | |
| 204 | 615342 | return Any(exprResult); | |
| 205 | } | ||
| 206 | |||
| 207 | // Get the evaluated symbol type of an expression node, that is stored as IVisitable* | ||
| 208 | 723193 | f<QualType> IRGenerator.getEvaluatedType(IVisitable* node) { | |
| 209 | 723193 | ASTExprNode* exprNode = asExprNode(getASTNode(node)); | |
| 210 | 723193 | return exprNode.getEvaluatedSymbolType(this.manIdx); | |
| 211 | } | ||
| 212 | |||
| 213 | // Check if an expression node, that is stored as IVisitable*, has a compile time value | ||
| 214 | 9792 | f<bool> IRGenerator.hasCompileTimeValue(IVisitable* node) { | |
| 215 | 9792 | ASTNode* astNode = getASTNode(node); | |
| 216 | 9792 | return astNode.hasCompileTimeValue(this.manIdx); | |
| 217 | } | ||
| 218 | |||
| 219 | // Get the compile time value of an expression node, that is stored as IVisitable* | ||
| 220 | 3822 | f<CompileTimeValue> IRGenerator.getCompileTimeValue(IVisitable* node) { | |
| 221 | 3822 | ASTNode* astNode = getASTNode(node); | |
| 222 | 3822 | return astNode.getCompileTimeValue(this.manIdx); | |
| 223 | } | ||
| 224 | |||
| 225 | 507170 | f<long> getEntryKey(const SymbolTableEntry* entry) { | |
| 226 | unsafe { | ||
| 227 | 507170 | return cast<long>(entry); | |
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | 46641 | f<long> getFunctionKey(const model::Function* function) { | |
| 232 | unsafe { | ||
| 233 | 46641 | return cast<long>(function); | |
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | // Abort the compilation with a compiler error | ||
| 238 | ✗ | p raiseCompilerError(const CodeLoc& codeLoc, CompilerErrorType errorType, const String& message) { | |
| 239 | ✗ | const CompilerError compilerError = CompilerError(codeLoc, errorType, message.getRaw()); | |
| 240 | ✗ | abortWithError(compilerError.getMessage()); | |
| 241 | ✗ | } | |
| 242 | |||
| 243 | // Abort the compilation with a semantic error | ||
| 244 | ✗ | p raiseSemanticError(const ASTNode* node, SemanticErrorType errorType, const String& message) { | |
| 245 | ✗ | const SemanticError semanticError = SemanticError(node, errorType, message.getRaw()); | |
| 246 | ✗ | abortWithError(semanticError.getMessage()); | |
| 247 | ✗ | } | |
| 248 | |||
| 249 | // Integer constants. The values are masked to the bit width, so that negative values of signed types are represented | ||
| 250 | // correctly. | ||
| 251 | 8874 | f<llvm::Value> IRGenerator.getInt8(long value) { | |
| 252 | 8874 | return llvm::getConstInt(this.builder.getInt8Ty(), cast<unsigned long>(value) & 255ul); | |
| 253 | } | ||
| 254 | |||
| 255 | 2253 | f<llvm::Value> IRGenerator.getInt16(long value) { | |
| 256 | 2253 | return llvm::getConstInt(this.builder.getInt16Ty(), cast<unsigned long>(value) & 65535ul); | |
| 257 | } | ||
| 258 | |||
| 259 | 107383 | f<llvm::Value> IRGenerator.getInt32(long value) { | |
| 260 | 107383 | return llvm::getConstInt(this.builder.getInt32Ty(), cast<unsigned long>(value) & 4294967295ul); | |
| 261 | } | ||
| 262 | |||
| 263 | 123315 | f<llvm::Value> IRGenerator.getInt64(long value) { | |
| 264 | 123315 | return llvm::getConstInt(this.builder.getInt64Ty(), cast<unsigned long>(value)); | |
| 265 | } | ||
| 266 | |||
| 267 | 7639 | f<llvm::Value> IRGenerator.getInt1(bool value) { | |
| 268 | 7639 | return value ? this.builder.getTrue() : this.builder.getFalse(); | |
| 269 | } | ||
| 270 | |||
| 271 | // ======================================================== IR generator ========================================================= | ||
| 272 | |||
| 273 | 3499 | public f<Any> IRGenerator.visitEntry(ASTEntryNode* node) { | |
| 274 | // Generate IR | ||
| 275 | 3499 | this.visitChildren(cast<ASTNode*>(node)); | |
| 276 | |||
| 277 | // Generate test main if required | ||
| 278 | 3499 | if this.sourceFile.isMainFile && this.cliOptions.generateTestMain { | |
| 279 | 9 | this.generateTestMain(); | |
| 280 | 9 | } | |
| 281 | |||
| 282 | // Execute deferred VTable initializations | ||
| 283 | 5053 | foreach const StructBase* spiceStruct : this.deferredVTableInitializations { | |
| 284 | 1554 | this.generateVTableInitializer(spiceStruct); | |
| 285 | 1554 | } | |
| 286 | |||
| 287 | // Finalize debug info generator | ||
| 288 | 3499 | this.diGenerator.finalize(); | |
| 289 | |||
| 290 | // Verify module | ||
| 291 | 3499 | this.verifyModule(node.codeLoc); | |
| 292 | |||
| 293 | 6998 | return Any(); | |
| 294 | 3499 | } | |
| 295 | |||
| 296 | 128764 | public f<llvm::Value> IRGenerator.insertAlloca(llvm::Type llvmType, const String& varName = String()) { | |
| 297 | 128764 | if !this.allocaInsertInst.isNull() { // If there is already an alloca inst, insert right after that | |
| 298 | // The alloca belongs after the last alloca. The LLVM C API cannot move instructions, so the alloca is created at its | ||
| 299 | // final position right away | ||
| 300 | // Positioning the builder before an instruction also takes over its debug location, so save and restore it | ||
| 301 | 84515 | const llvm::BasicBlock currentBlock = this.builder.getInsertBlock(); | |
| 302 | 84515 | const llvm::Metadata currentDebugLoc = this.builder.getCurrentDebugLocation(); | |
| 303 | 84515 | const llvm::Value nextInst = this.allocaInsertInst.getNextInstruction(); | |
| 304 | 84515 | if nextInst.isNull() { | |
| 305 | 643 | this.builder.setInsertPoint(this.allocaInsertBlock); | |
| 306 | 643 | } else { | |
| 307 | 83872 | this.builder.setInsertPointBefore(this.allocaInsertBlock, nextInst); | |
| 308 | 83872 | } | |
| 309 | 84515 | llvm::Value allocaInst = this.builder.createAlloca(llvmType, varName.getRaw()); | |
| 310 | 84515 | allocaInst.dropLocation(); // Part of prologue | |
| 311 | 84515 | this.allocaInsertInst = allocaInst; | |
| 312 | 84515 | this.builder.setInsertPoint(currentBlock); | |
| 313 | 84515 | this.builder.setCurrentDebugLocation(currentDebugLoc); | |
| 314 | 84515 | } else { // This is the first alloca inst in the current function -> insert at the entry block | |
| 315 | // Save current basic block and debug location and move insert cursor to entry block of the current function | ||
| 316 | 44249 | const llvm::BasicBlock currentBlock = this.builder.getInsertBlock(); | |
| 317 | 44249 | const llvm::Metadata currentDebugLoc = this.builder.getCurrentDebugLocation(); | |
| 318 | 44249 | const llvm::Value firstInst = this.allocaInsertBlock.getFirstInstruction(); | |
| 319 | 44249 | if firstInst.isNull() { | |
| 320 | 44132 | this.builder.setInsertPoint(this.allocaInsertBlock); | |
| 321 | 44132 | } else { | |
| 322 | 117 | this.builder.setInsertPointBefore(this.allocaInsertBlock, firstInst); | |
| 323 | 117 | } | |
| 324 | |||
| 325 | // Allocate the size of the given LLVM type | ||
| 326 | 44249 | this.allocaInsertInst = this.builder.createAlloca(llvmType, varName.getRaw()); | |
| 327 | 44249 | this.allocaInsertInst.dropLocation(); // Part of prologue | |
| 328 | |||
| 329 | // Restore old basic block and debug location | ||
| 330 | 44249 | this.builder.setInsertPoint(currentBlock); | |
| 331 | 44249 | this.builder.setCurrentDebugLocation(currentDebugLoc); | |
| 332 | 44249 | } | |
| 333 | |||
| 334 | // Insert lifetime start marker | ||
| 335 | 128764 | if this.cliOptions.useLifetimeMarkers { | |
| 336 | 3864 | this.createLifetimeMarker("llvm.lifetime.start", this.allocaInsertInst); | |
| 337 | 3864 | } | |
| 338 | |||
| 339 | 151822 | return this.allocaInsertInst; | |
| 340 | 23058 | } | |
| 341 | |||
| 342 | 102792 | public f<llvm::Value> IRGenerator.insertAlloca(const QualType& qualType, const String& varName = String()) { | |
| 343 | 102792 | const llvm::Type llvmType = qualType.toLLVMType(this.sourceFile); | |
| 344 | 102792 | const llvm::Value alloca = this.insertAlloca(llvmType, varName); | |
| 345 | |||
| 346 | // Insert type metadata | ||
| 347 | 102792 | if this.cliOptions.useTBAAMetadata { | |
| 348 | 12 | this.mdGenerator.generateTypeMetadata(this.allocaInsertInst, qualType); | |
| 349 | 12 | } | |
| 350 | |||
| 351 | 129824 | return alloca; | |
| 352 | 27032 | } | |
| 353 | |||
| 354 | // Like builder.CreateLifetimeStart/CreateLifetimeEnd | ||
| 355 | 5322 | p IRGenerator.createLifetimeMarker(string intrinsicName, llvm::Value ptr) { | |
| 356 | 5322 | Vector<llvm::Type> overloadTypes; | |
| 357 | 5322 | overloadTypes.pushBack(this.builder.getPtrTy()); | |
| 358 | 5322 | const llvm::Function markerFct = this.module.getIntrinsicDeclaration(intrinsicName, overloadTypes); | |
| 359 | 5322 | Vector<llvm::Value> args; | |
| 360 | 5322 | args.pushBack(ptr); | |
| 361 | 5322 | this.builder.createCall(markerFct, args); | |
| 362 | 5322 | } | |
| 363 | |||
| 364 | 333216 | public f<llvm::Value> IRGenerator.insertLoad(llvm::Type llvmType, llvm::Value ptr, bool isVolatile = false, const String& varName = String()) { | |
| 365 | 333216 | const llvm::Type ptrType = ptr.getType(); | |
| 366 | 333216 | assert ptrType.isPointerTy(); | |
| 367 | 377415 | return this.builder.createLoad(ptr, llvmType, varName.getRaw(), isVolatile); | |
| 368 | 44199 | } | |
| 369 | |||
| 370 | 288559 | public f<llvm::Value> IRGenerator.insertLoad(const QualType& qualType, llvm::Value ptr, bool isVolatile = false, const String& varName = String()) { | |
| 371 | 288559 | const llvm::Type llvmType = qualType.toLLVMType(this.sourceFile); | |
| 372 | 288559 | const llvm::Value load = this.insertLoad(llvmType, ptr, isVolatile, varName); | |
| 373 | 288559 | if this.cliOptions.useTBAAMetadata { | |
| 374 | 12 | this.mdGenerator.generateTBAAMetadata(load, qualType); | |
| 375 | 12 | } | |
| 376 | 577118 | return load; | |
| 377 | 288559 | } | |
| 378 | |||
| 379 | 166893 | #[ignoreUnusedReturnValue] | |
| 380 | 166893 | public f<llvm::Value> IRGenerator.insertStore(llvm::Value val, llvm::Value ptr, bool isVolatile = false) { | |
| 381 | 166893 | const llvm::Type ptrType = ptr.getType(); | |
| 382 | 166893 | assert ptrType.isPointerTy(); | |
| 383 | 166893 | return this.builder.createStore(val, ptr, isVolatile); | |
| 384 | } | ||
| 385 | |||
| 386 | 50417 | public p IRGenerator.insertStore(llvm::Value val, llvm::Value ptr, const QualType& qualType, bool isVolatile = false) { | |
| 387 | 50417 | const llvm::Value store = this.insertStore(val, ptr, isVolatile); | |
| 388 | 50417 | if this.cliOptions.useTBAAMetadata { | |
| 389 | 12 | this.mdGenerator.generateTBAAMetadata(store, qualType); | |
| 390 | 12 | } | |
| 391 | 50417 | } | |
| 392 | |||
| 393 | 93553 | public f<llvm::Value> IRGenerator.insertInBoundsGEP(llvm::Type ty, llvm::Value basePtr, const Vector<llvm::Value>& indices, const String& varName = String()) { | |
| 394 | 93553 | const llvm::Type basePtrType = basePtr.getType(); | |
| 395 | 93553 | assert basePtrType.isPointerTy(); | |
| 396 | 93553 | assert !indices.isEmpty(); | |
| 397 | 271688 | foreach const llvm::Value& index : indices { | |
| 398 | 178135 | const llvm::Type indexType = index.getType(); | |
| 399 | 178135 | assert indexType.isIntegerTy(32) || indexType.isIntegerTy(64); | |
| 400 | 178135 | } | |
| 401 | |||
| 402 | // Insert GEP | ||
| 403 | 96363 | return this.builder.createInBoundsGEP(ty, basePtr, indices, varName.getRaw()); | |
| 404 | 2810 | } | |
| 405 | |||
| 406 | 10162 | public f<llvm::Value> IRGenerator.insertInBoundsGEP(llvm::Type ty, llvm::Value basePtr, llvm::Value index, const String& varName = String()) { | |
| 407 | 10162 | Vector<llvm::Value> indices; | |
| 408 | 10162 | indices.pushBack(index); | |
| 409 | 20324 | return this.insertInBoundsGEP(ty, basePtr, indices, varName); | |
| 410 | 10162 | } | |
| 411 | |||
| 412 | 27114 | public f<llvm::Value> IRGenerator.insertStructGEP(llvm::Type ty, llvm::Value basePtr, unsigned int index, const String& varName = String()) { | |
| 413 | 27114 | const llvm::Type basePtrType = basePtr.getType(); | |
| 414 | 27114 | assert basePtrType.isPointerTy(); | |
| 415 | |||
| 416 | // If we use index 0 we can use the base pointer directly | ||
| 417 | 27114 | if index == 0 { | |
| 418 | 15594 | return basePtr; | |
| 419 | } | ||
| 420 | |||
| 421 | // Insert GEP | ||
| 422 | 38391 | return this.builder.createStructGEP(ty, basePtr, index, varName.getRaw()); | |
| 423 | 26871 | } | |
| 424 | |||
| 425 | 187458 | #[ignoreUnusedReturnValue] | |
| 426 | 187458 | public f<llvm::Value> IRGenerator.resolveValue(IVisitable* node) { | |
| 427 | // Visit the given AST node | ||
| 428 | 187458 | LLVMExprResult exprResult = this.visitExpr(node); | |
| 429 | 187458 | return this.resolveValue(node, exprResult); | |
| 430 | } | ||
| 431 | |||
| 432 | /** | ||
| 433 | * Resolve the value of an expression in its expression scope, if it has one, and destruct the temporaries of the expression right | ||
| 434 | * afterwards. The value is already loaded at that point, so it does not depend on the temporaries anymore. | ||
| 435 | * | ||
| 436 | * @param expr Expression to resolve the value of | ||
| 437 | * @return Value of the expression | ||
| 438 | */ | ||
| 439 | 39531 | public f<llvm::Value> IRGenerator.resolveValueInExprScope(IVisitable* expr) { | |
| 440 | 39531 | ExprScopeHandle exprScopeHandle = ExprScopeHandle(&this.compilerPass, expr); | |
| 441 | 39531 | const llvm::Value value = this.resolveValue(expr); | |
| 442 | 39531 | const Scope* exprScope = exprScopeHandle.getExprScope(); | |
| 443 | 39531 | if exprScope != nil<Scope*> { | |
| 444 | 39519 | this.generateTemporariesCleanup(exprScope, getASTNode(expr)); | |
| 445 | 39519 | } | |
| 446 | 79062 | return value; | |
| 447 | 39531 | } | |
| 448 | |||
| 449 | 209990 | #[ignoreUnusedReturnValue] | |
| 450 | 209990 | public f<llvm::Value> IRGenerator.resolveValue(IVisitable* node, LLVMExprResult& exprResult) { | |
| 451 | 209990 | return this.resolveValue(this.getEvaluatedType(node), exprResult); | |
| 452 | } | ||
| 453 | |||
| 454 | 414754 | #[ignoreUnusedReturnValue] | |
| 455 | 414754 | public f<llvm::Value> IRGenerator.resolveValue(const QualType& qualType, LLVMExprResult& exprResult) { | |
| 456 | // Check if the value is already present | ||
| 457 | 414754 | if !exprResult.value.isNull() { | |
| 458 | 127469 | return exprResult.value; | |
| 459 | } | ||
| 460 | |||
| 461 | // Check if a constant is present | ||
| 462 | 287285 | if !exprResult.constant.isNull() { | |
| 463 | 84475 | materializeConstant(exprResult); | |
| 464 | 84475 | return exprResult.value; | |
| 465 | } | ||
| 466 | |||
| 467 | 202810 | assert !exprResult.ptr.isNull() || !exprResult.refPtr.isNull(); | |
| 468 | |||
| 469 | // De-reference if reference type | ||
| 470 | 202810 | const bool isVolatile = exprResult.entry != nil<const SymbolTableEntry*> && exprResult.entry.isVolatile; | |
| 471 | 202810 | if !exprResult.refPtr.isNull() && exprResult.ptr.isNull() { | |
| 472 | 2636 | exprResult.ptr = this.insertLoad(this.builder.getPtrTy(), exprResult.refPtr, isVolatile); | |
| 473 | 2636 | } | |
| 474 | |||
| 475 | // Load the value from the pointer | ||
| 476 | 202810 | const QualType referencedType = qualType.removeReferenceWrapper(); | |
| 477 | 202810 | exprResult.value = this.insertLoad(referencedType, exprResult.ptr, isVolatile); | |
| 478 | |||
| 479 | 202810 | return exprResult.value; | |
| 480 | } | ||
| 481 | |||
| 482 | 16813 | #[ignoreUnusedReturnValue] | |
| 483 | 16813 | public f<llvm::Value> IRGenerator.resolveAddress(IVisitable* node) { | |
| 484 | // Visit the given AST node | ||
| 485 | 16813 | LLVMExprResult exprResult = this.visitExpr(node); | |
| 486 | 16813 | return this.resolveAddress(exprResult); | |
| 487 | } | ||
| 488 | |||
| 489 | 141191 | #[ignoreUnusedReturnValue] | |
| 490 | 141191 | public f<llvm::Value> IRGenerator.resolveAddress(LLVMExprResult& exprResult) { | |
| 491 | // Check if an address is already present | ||
| 492 | 141191 | if !exprResult.ptr.isNull() { | |
| 493 | 119032 | return exprResult.ptr; | |
| 494 | } | ||
| 495 | |||
| 496 | // Check if the reference address is already present | ||
| 497 | 22159 | const bool isVolatile = exprResult.entry != nil<const SymbolTableEntry*> && exprResult.entry.isVolatile; | |
| 498 | 22159 | if !exprResult.refPtr.isNull() && exprResult.ptr.isNull() { | |
| 499 | 20527 | exprResult.ptr = this.insertLoad(this.builder.getPtrTy(), exprResult.refPtr, isVolatile); | |
| 500 | 20527 | return exprResult.ptr; | |
| 501 | } | ||
| 502 | |||
| 503 | // If not, store the value or constant | ||
| 504 | 1632 | materializeConstant(exprResult); | |
| 505 | 1632 | assert !exprResult.value.isNull(); | |
| 506 | 1632 | const String varName = exprResult.entry != nil<const SymbolTableEntry*> ? exprResult.entry.name : String(); | |
| 507 | 1632 | exprResult.ptr = this.insertAlloca(exprResult.value.getType(), varName); | |
| 508 | 1632 | this.insertStore(exprResult.value, exprResult.ptr, isVolatile); | |
| 509 | |||
| 510 | 3264 | return exprResult.ptr; | |
| 511 | 1632 | } | |
| 512 | |||
| 513 | // Resolve a resolver, see Resolver | ||
| 514 | 9790 | public f<llvm::Value> IRGenerator.resolve(const Resolver& resolver) { | |
| 515 | 9790 | switch resolver.kind { | |
| 516 | ✗ | case ResolverKind::NONE: { return llvm::Value(); } | |
| 517 | 1341 | case ResolverKind::VALUE: { return this.resolveValue(resolver.qualType, *resolver.exprResult); } | |
| 518 | 6847 | case ResolverKind::ADDRESS: { return this.resolveAddress(*resolver.exprResult); } | |
| 519 | 896 | case ResolverKind::EXPR_VALUE: { return this.resolveValue(resolver.expr); } | |
| 520 | 28 | case ResolverKind::EXPR_ADDRESS: { return this.resolveAddress(resolver.expr); } | |
| 521 | 678 | case ResolverKind::FIXED: { return resolver.fixedValue; } | |
| 522 | } | ||
| 523 | ✗ | } | |
| 524 | |||
| 525 | /** | ||
| 526 | * Pack a scalar compile-time constant (integer, float, or a null pointer/aggregate) into a raw byte-array constant of | ||
| 527 | * the given type, using the target's endianness. This is used to embed a union's default field value into its | ||
| 528 | * byte-buffer payload without needing a bitcast between unrelated LLVM types (which is not something opaque-pointer- | ||
| 529 | * era LLVM supports between arbitrary aggregate/scalar types). | ||
| 530 | * | ||
| 531 | * Non-null pointer-shaped constants (e.g. a string literal's global address) cannot be represented this way, since | ||
| 532 | * their real value is only known at link/load time (a relocation) and a plain byte array cannot carry one. | ||
| 533 | * | ||
| 534 | * @param value Compile-time constant to pack | ||
| 535 | * @param byteArrayType Target byte-array type (element type i8) | ||
| 536 | * @return Packed byte-array constant | ||
| 537 | */ | ||
| 538 | 2 | f<llvm::Value> IRGenerator.packConstantAsByteArray(llvm::Value value, llvm::Type byteArrayType) { | |
| 539 | 2 | const llvm::Type elementType = byteArrayType.getArrayElementType(); | |
| 540 | 2 | assert elementType.isIntegerTy(8); | |
| 541 | 2 | const unsigned long numBytes = byteArrayType.getArrayNumElements(); | |
| 542 | |||
| 543 | // A null/zero constant (e.g. a null pointer, or a zero-initialized aggregate) packs trivially as all-zero bytes. | ||
| 544 | 2 | if value.isNullValue() { | |
| 545 | ✗ | return llvm::getNullValue(byteArrayType); | |
| 546 | } | ||
| 547 | |||
| 548 | // Spice constants have at most 64 bits, so this works on the plain 64 bit value instead of an arbitrary precision integer. | ||
| 549 | 2 | unsigned long bits = 0l; | |
| 550 | 2 | if value.isConstantInt() { | |
| 551 | 2 | bits = value.getZExtValue(); | |
| 552 | 2 | } else if value.isConstantFP() { | |
| 553 | ✗ | const double doubleValue = value.getDoubleValue(); | |
| 554 | unsafe { | ||
| 555 | ✗ | const unsigned long* bitsPtr = cast<unsigned long*>(&doubleValue); | |
| 556 | ✗ | bits = *bitsPtr; | |
| 557 | } | ||
| 558 | ✗ | } else { | |
| 559 | // See the function comment: this is a rare case in practice (most union field defaults are numeric), so it is | ||
| 560 | // deliberately unsupported for now rather than silently producing a wrong default value. | ||
| 561 | − | panic(Error("Unsupported default value for a union field of this type")); // LCOV_EXCL_LINE | |
| 562 | ✗ | } | |
| 563 | |||
| 564 | 2 | const bool isLittleEndian = this.module.isLittleEndian(); | |
| 565 | 2 | Vector<char> bytes; | |
| 566 | 18 | for unsigned long i = 0l; i < numBytes; i++ { | |
| 567 | 16 | bytes.pushBack('\0'); | |
| 568 | 16 | } | |
| 569 | 18 | for unsigned long i = 0l; i < numBytes; i++ { | |
| 570 | 16 | const unsigned long byteIndex = isLittleEndian ? i : numBytes - 1l - i; | |
| 571 | 16 | const unsigned long byteValue = i < 8l ? (bits >> (i * 8l)) & 255ul : 0ul; | |
| 572 | 16 | char& byteSlot = bytes.get(byteIndex); | |
| 573 | 16 | byteSlot = cast<char>(byteValue); | |
| 574 | 16 | } | |
| 575 | |||
| 576 | 4 | return llvm::getConstByteArray(*this.context, bytes); | |
| 577 | 2 | } | |
| 578 | |||
| 579 | 7539 | public f<llvm::Value> IRGenerator.getDefaultValueForSymbolType(const QualType& symbolType) { | |
| 580 | // Double | ||
| 581 | 7539 | if symbolType.is(SuperType::TY_DOUBLE) { | |
| 582 | 54 | return llvm::getConstDouble(this.builder.getDoubleTy(), 0.0); | |
| 583 | } | ||
| 584 | |||
| 585 | // Int | ||
| 586 | 7485 | if symbolType.is(SuperType::TY_INT) { | |
| 587 | 1430 | return this.getInt32(0l); | |
| 588 | } | ||
| 589 | |||
| 590 | // Short | ||
| 591 | 6055 | if symbolType.is(SuperType::TY_SHORT) { | |
| 592 | 34 | return this.getInt16(0l); | |
| 593 | } | ||
| 594 | |||
| 595 | // Long | ||
| 596 | 6021 | if symbolType.is(SuperType::TY_LONG) { | |
| 597 | 1224 | return this.getInt64(0l); | |
| 598 | } | ||
| 599 | |||
| 600 | // Byte or char | ||
| 601 | 4797 | if symbolType.is(SuperType::TY_BYTE) || symbolType.is(SuperType::TY_CHAR) { | |
| 602 | 117 | return this.getInt8(0l); | |
| 603 | } | ||
| 604 | |||
| 605 | // String | ||
| 606 | 4680 | if symbolType.is(SuperType::TY_STRING) { | |
| 607 | 1234 | llvm::Value globalString = this.module.createGlobalString(String(""), ""); | |
| 608 | 1234 | if this.cliOptions.comparableOutput { | |
| 609 | 1234 | globalString.setAlignment(4); | |
| 610 | 1234 | } | |
| 611 | 2468 | return globalString; | |
| 612 | 1234 | } | |
| 613 | |||
| 614 | // Bool | ||
| 615 | 3446 | if symbolType.is(SuperType::TY_BOOL) { | |
| 616 | 216 | return this.builder.getFalse(); | |
| 617 | } | ||
| 618 | |||
| 619 | // Pointer or reference | ||
| 620 | 3230 | if symbolType.is(SuperType::TY_PTR) || symbolType.is(SuperType::TY_REF) { | |
| 621 | 2225 | return llvm::getNullValue(this.builder.getPtrTy()); | |
| 622 | } | ||
| 623 | |||
| 624 | // Array | ||
| 625 | 1005 | if symbolType.isArray() { | |
| 626 | // Get array size | ||
| 627 | 129 | const unsigned long arraySize = cast<unsigned long>(symbolType.getArraySize()); | |
| 628 | |||
| 629 | // Get default value for item | ||
| 630 | 129 | const QualType itemSymbolType = symbolType.getContained(); | |
| 631 | 129 | const llvm::Value defaultItemValue = this.getDefaultValueForSymbolType(itemSymbolType); | |
| 632 | |||
| 633 | // Retrieve array and item type | ||
| 634 | 129 | const llvm::Type itemType = itemSymbolType.toLLVMType(this.sourceFile); | |
| 635 | |||
| 636 | // Create a constant array with n times the default value | ||
| 637 | 129 | Vector<llvm::Value> itemConstants; | |
| 638 | 120702 | for unsigned long i = 0l; i < arraySize; i++ { | |
| 639 | 120573 | itemConstants.pushBack(defaultItemValue); | |
| 640 | 120573 | } | |
| 641 | 258 | return llvm::getConstArray(itemType, itemConstants); | |
| 642 | 129 | } | |
| 643 | |||
| 644 | // Function or procedure | ||
| 645 | 876 | if symbolType.is(SuperType::TY_FUNCTION) || symbolType.is(SuperType::TY_PROCEDURE) { | |
| 646 | 153 | const llvm::Value ptrDefaultValue = this.getDefaultValueForSymbolType(QualType(SuperType::TY_PTR)); | |
| 647 | 153 | const llvm::Value sizeDefaultValue = this.getInt64(0l); | |
| 648 | 153 | Vector<llvm::Value> fieldValues; | |
| 649 | 153 | fieldValues.pushBack(ptrDefaultValue); | |
| 650 | 153 | fieldValues.pushBack(ptrDefaultValue); | |
| 651 | 153 | fieldValues.pushBack(sizeDefaultValue); | |
| 652 | 306 | return llvm::getConstNamedStruct(this.llvmTypes.lambdaFatPtrType, fieldValues); | |
| 653 | 153 | } | |
| 654 | |||
| 655 | // Struct | ||
| 656 | 723 | if symbolType.is(SuperType::TY_STRUCT) { | |
| 657 | // Retrieve field count | ||
| 658 | 668 | Scope* structScope = symbolType.getBodyScope(); | |
| 659 | 668 | assert structScope != nil<Scope*>; | |
| 660 | 668 | const unsigned long fieldCount = structScope.getFieldCount(); | |
| 661 | |||
| 662 | // Get default values for all fields of the struct | ||
| 663 | 668 | Vector<llvm::Value> fieldConstants; | |
| 664 | 668 | fieldConstants.reserve(fieldCount + 1l); | |
| 665 | |||
| 666 | // Add the VTable address point for the synthesized vtable pointer if required | ||
| 667 | 668 | const Struct* spiceStruct = symbolType.getStruct(nil<ASTNode*>); | |
| 668 | 668 | assert spiceStruct != nil<const Struct*>; | |
| 669 | 668 | if spiceStruct.hasSynthesizedVTablePtr() { | |
| 670 | 2 | fieldConstants.pushBack(this.getVTableAddressPoint(&spiceStruct.structBase)); | |
| 671 | 2 | } | |
| 672 | |||
| 673 | // Add default value for each struct field | ||
| 674 | 2524 | for unsigned long i = 0l; i < fieldCount; i++ { | |
| 675 | // Get entry of the field | ||
| 676 | 1856 | const SymbolTableEntry* fieldEntry = structScope.lookupField(cast<unsigned int>(i)); | |
| 677 | 1856 | assert fieldEntry != nil<const SymbolTableEntry*> && fieldEntry.isField(); | |
| 678 | |||
| 679 | // Retrieve default field value | ||
| 680 | 1856 | llvm::Value defaultFieldValue; | |
| 681 | 1856 | const ASTFieldNode* fieldNode = getFieldNode(fieldEntry); | |
| 682 | 1856 | if fieldNode != nil<const ASTFieldNode*> && fieldNode.defaultValue != nil<IVisitable*> { | |
| 683 | 1463 | const CompileTimeValue defaultValue = this.getCompileTimeValue(fieldNode.defaultValue); | |
| 684 | 1463 | defaultFieldValue = this.getConst(defaultValue, fieldEntry.getQualType(), cast<ASTNode*>(fieldNode)); | |
| 685 | 1463 | } else { | |
| 686 | 393 | defaultFieldValue = this.getDefaultValueForSymbolType(fieldEntry.getQualType()); | |
| 687 | 393 | } | |
| 688 | |||
| 689 | 1856 | fieldConstants.pushBack(defaultFieldValue); | |
| 690 | 1856 | } | |
| 691 | |||
| 692 | 668 | const llvm::Type structType = symbolType.toLLVMType(this.sourceFile); | |
| 693 | 1336 | return llvm::getConstNamedStruct(structType, fieldConstants); | |
| 694 | 668 | } | |
| 695 | |||
| 696 | // Union | ||
| 697 | 55 | if symbolType.is(SuperType::TY_UNION) { | |
| 698 | 21 | Scope* unionScope = symbolType.getBodyScope(); | |
| 699 | 21 | assert unionScope != nil<Scope*>; | |
| 700 | 21 | const unsigned long fieldCount = unionScope.getFieldCount(); | |
| 701 | |||
| 702 | // Find the (at most one) field with a default value. Tag 0 is reserved for the "unset" state (see the matching | ||
| 703 | // comment in IRGenerator::visitPostfixUnaryExpr's TY_UNION branch), so a field's tag is its index shifted by one. | ||
| 704 | 21 | unsigned int defaultFieldTag = 0; // Sentinel: no field is active ("unset" state) | |
| 705 | 21 | llvm::Value defaultFieldValue; | |
| 706 | 67 | for unsigned long i = 0l; i < fieldCount; i++ { | |
| 707 | 48 | const SymbolTableEntry* fieldEntry = unionScope.lookupField(cast<unsigned int>(i)); | |
| 708 | 48 | assert fieldEntry != nil<const SymbolTableEntry*> && fieldEntry.isField(); | |
| 709 | 48 | const ASTFieldNode* fieldNode = getFieldNode(fieldEntry); | |
| 710 | 48 | if fieldNode != nil<const ASTFieldNode*> && fieldNode.defaultValue != nil<IVisitable*> { | |
| 711 | 2 | defaultFieldTag = cast<unsigned int>(i) + 1; | |
| 712 | 2 | const CompileTimeValue defaultValue = this.getCompileTimeValue(fieldNode.defaultValue); | |
| 713 | 2 | defaultFieldValue = this.getConst(defaultValue, fieldEntry.getQualType(), cast<ASTNode*>(fieldNode)); | |
| 714 | 2 | break; | |
| 715 | } | ||
| 716 | 46 | } | |
| 717 | |||
| 718 | 21 | const llvm::Type unionType = symbolType.toLLVMType(this.sourceFile); | |
| 719 | |||
| 720 | // With no default field, tag 0 and an all-zero payload is exactly the "unset" state, so the whole constant | ||
| 721 | // collapses to a plain zero value. This also makes such a union eligible for zero-initialized (.bss) storage | ||
| 722 | // when used as a global, rather than requiring an explicit non-zero initializer. | ||
| 723 | 21 | if defaultFieldValue.isNull() { | |
| 724 | 19 | return llvm::getNullValue(unionType); | |
| 725 | } | ||
| 726 | |||
| 727 | 2 | const llvm::Value tagConstant = this.getInt32(cast<long>(defaultFieldTag)); | |
| 728 | 2 | const llvm::Value alignPadConstant = llvm::getNullValue(unionType.getStructElementType(1)); | |
| 729 | 2 | const llvm::Type payloadType = unionType.getStructElementType(2); | |
| 730 | 2 | const llvm::Value payloadConstant = this.packConstantAsByteArray(defaultFieldValue, payloadType); | |
| 731 | |||
| 732 | 2 | Vector<llvm::Value> fieldValues; | |
| 733 | 2 | fieldValues.pushBack(tagConstant); | |
| 734 | 2 | fieldValues.pushBack(alignPadConstant); | |
| 735 | 2 | fieldValues.pushBack(payloadConstant); | |
| 736 | 4 | return llvm::getConstNamedStruct(unionType, fieldValues); | |
| 737 | 2 | } | |
| 738 | |||
| 739 | // Interface | ||
| 740 | 34 | if symbolType.is(SuperType::TY_INTERFACE) { | |
| 741 | 34 | const llvm::Type structType = symbolType.toLLVMType(this.sourceFile); | |
| 742 | 34 | Vector<llvm::Value> fieldValues; | |
| 743 | 34 | fieldValues.pushBack(llvm::getNullValue(this.builder.getPtrTy())); | |
| 744 | 68 | return llvm::getConstNamedStruct(structType, fieldValues); | |
| 745 | 34 | } | |
| 746 | |||
| 747 | − | panic(Error("Cannot determine default value for symbol type")); // LCOV_EXCL_LINE | |
| 748 | } | ||
| 749 | |||
| 750 | // Get the field node, a field entry is declared by, if it is declared by a field node | ||
| 751 | 1904 | f<const ASTFieldNode*> getFieldNode(const SymbolTableEntry* fieldEntry) { | |
| 752 | 1904 | const ASTNode* declNode = fieldEntry.declNode; | |
| 753 | 1904 | if declNode == nil<const ASTNode*> || declNode.kind != ASTNodeKind::FIELD { | |
| 754 | 33 | return nil<const ASTFieldNode*>; | |
| 755 | } | ||
| 756 | 1871 | return downcast<ASTFieldNode>(declNode); | |
| 757 | } | ||
| 758 | |||
| 759 | 80651 | f<llvm::Value> IRGenerator.getConst(const CompileTimeValue& compileTimeValue, const QualType& qualType, const ASTNode* node) { | |
| 760 | 80651 | if qualType.is(SuperType::TY_DOUBLE) { | |
| 761 | 1331 | return llvm::getConstDouble(this.builder.getDoubleTy(), compileTimeValue.getDoubleValue()); | |
| 762 | } | ||
| 763 | |||
| 764 | 79320 | if qualType.is(SuperType::TY_INT) { | |
| 765 | 14268 | return this.getInt32(cast<long>(compileTimeValue.getIntValue())); | |
| 766 | } | ||
| 767 | |||
| 768 | 65052 | if qualType.is(SuperType::TY_SHORT) { | |
| 769 | 2219 | return this.getInt16(cast<long>(compileTimeValue.getShortValue())); | |
| 770 | } | ||
| 771 | |||
| 772 | 62833 | if qualType.is(SuperType::TY_LONG) { | |
| 773 | 37117 | return this.getInt64(compileTimeValue.getLongValue()); | |
| 774 | } | ||
| 775 | |||
| 776 | 25716 | if qualType.is(SuperType::TY_BYTE) || qualType.is(SuperType::TY_CHAR) { | |
| 777 | 8757 | return this.getInt8(cast<long>(cast<int>(compileTimeValue.getCharValue()))); | |
| 778 | } | ||
| 779 | |||
| 780 | 16959 | if qualType.is(SuperType::TY_STRING) { | |
| 781 | 10969 | const String& stringValue = this.resourceManager.compileTimeStringValues.get(compileTimeValue.getStringValueOffset()); | |
| 782 | 21938 | return this.createGlobalStringConst(String(ANON_GLOBAL_STRING_NAME), stringValue, node.codeLoc); | |
| 783 | 10969 | } | |
| 784 | |||
| 785 | 5990 | if qualType.is(SuperType::TY_BOOL) { | |
| 786 | 5503 | return this.getInt1(compileTimeValue.getBoolValue()); | |
| 787 | } | ||
| 788 | |||
| 789 | 487 | if qualType.is(SuperType::TY_PTR) { | |
| 790 | 481 | return llvm::getNullValue(this.builder.getPtrTy()); | |
| 791 | } | ||
| 792 | |||
| 793 | // The only compile-time value of a function or procedure type is nil, which is represented as a null fat pointer | ||
| 794 | 6 | if qualType.is(SuperType::TY_FUNCTION) || qualType.is(SuperType::TY_PROCEDURE) { | |
| 795 | 6 | return llvm::getNullValue(this.llvmTypes.lambdaFatPtrType); | |
| 796 | } | ||
| 797 | |||
| 798 | − | panic(Error("Constant fall-through")); // LCOV_EXCL_LINE | |
| 799 | } | ||
| 800 | |||
| 801 | 153300 | f<llvm::BasicBlock> IRGenerator.createBlock(const String& blockName = String()) { | |
| 802 | 198044 | return llvm::BasicBlock(*this.context, blockName.getRaw()); | |
| 803 | 44744 | } | |
| 804 | |||
| 805 | 153300 | p IRGenerator.switchToBlock(llvm::BasicBlock block, llvm::Function parentFct = llvm::Function()) { | |
| 806 | 153300 | assert !block.hasParent(); // Ensure that the block was not added to a function already | |
| 807 | // If no parent function were passed, use the current function | ||
| 808 | 153300 | if parentFct.isNull() { | |
| 809 | 108513 | const llvm::BasicBlock insertBlock = this.builder.getInsertBlock(); | |
| 810 | 108513 | parentFct = insertBlock.getParent(); | |
| 811 | 108513 | } | |
| 812 | // Append block to current function | ||
| 813 | 153300 | parentFct.pushBack(block); | |
| 814 | // Set insert point to the block | ||
| 815 | 153300 | this.builder.setInsertPoint(block); | |
| 816 | 153300 | this.blockAlreadyTerminated = false; | |
| 817 | 153300 | } | |
| 818 | |||
| 819 | 2933 | p IRGenerator.terminateBlock(ASTStmtLstNode* stmtLstNode) { | |
| 820 | 2933 | this.generateScopeCleanup(stmtLstNode); | |
| 821 | 2933 | this.blockAlreadyTerminated = true; | |
| 822 | 2933 | } | |
| 823 | |||
| 824 | 58912 | p IRGenerator.insertJump(llvm::BasicBlock targetBlock) { | |
| 825 | 58912 | if this.blockAlreadyTerminated { | |
| 826 | 20045 | return; | |
| 827 | } | ||
| 828 | 38867 | this.builder.createBr(targetBlock); | |
| 829 | 38867 | this.blockAlreadyTerminated = true; | |
| 830 | 38867 | } | |
| 831 | |||
| 832 | 46760 | p IRGenerator.insertCondJump(llvm::Value condition, llvm::BasicBlock trueBlock, llvm::BasicBlock falseBlock, Likelihood likelihood = Likelihood::UNSPECIFIED) { | |
| 833 | 46760 | if this.blockAlreadyTerminated { | |
| 834 | ✗ | return; | |
| 835 | } | ||
| 836 | 46760 | const llvm::Value jumpInst = this.builder.createCondBr(condition, trueBlock, falseBlock); | |
| 837 | 46760 | this.blockAlreadyTerminated = true; | |
| 838 | |||
| 839 | 46760 | if likelihood != Likelihood::UNSPECIFIED { | |
| 840 | 4911 | this.mdGenerator.generateBranchWeightsMetadata(jumpInst, likelihood); | |
| 841 | 4911 | } | |
| 842 | 46760 | } | |
| 843 | |||
| 844 | 44744 | p IRGenerator.verifyFunction(llvm::Function fct, const CodeLoc& codeLoc) { | |
| 845 | // Skip the verifying step if the verifier was disabled manually or debug info is emitted | ||
| 846 | 44744 | if this.cliOptions.disableVerifier { | |
| 847 | ✗ | return; | |
| 848 | } | ||
| 849 | |||
| 850 | // Verify function. The LLVM C API does not return the output of the function verifier, so it is printed to stderr | ||
| 851 | − | if llvm::verifyFunction(fct, llvm::LLVMVerifierFailureAction::PrintMessageAction) { // LCOV_EXCL_LINE | |
| 852 | − | raiseCompilerError(codeLoc, CompilerErrorType::INVALID_FUNCTION, String("Function verification failed")); // LCOV_EXCL_LINE | |
| 853 | ✗ | } | |
| 854 | 44744 | } | |
| 855 | |||
| 856 | 3499 | p IRGenerator.verifyModule(const CodeLoc& codeLoc) { | |
| 857 | // Skip the verifying step if the verifier was disabled manually or debug info is emitted | ||
| 858 | 3499 | if this.cliOptions.disableVerifier { | |
| 859 | ✗ | return; | |
| 860 | } | ||
| 861 | |||
| 862 | // Verify module | ||
| 863 | 3499 | String output; | |
| 864 | − | if this.module.verify(output) { // LCOV_EXCL_LINE | |
| 865 | − | raiseCompilerError(codeLoc, CompilerErrorType::INVALID_MODULE, output); // LCOV_EXCL_LINE | |
| 866 | ✗ | } | |
| 867 | 3499 | } | |
| 868 | |||
| 869 | 26703 | #[ignoreUnusedReturnValue] | |
| 870 | 26703 | f<LLVMExprResult> IRGenerator.doAssignment(IVisitable* lhsNode, IVisitable* rhsNode, ASTNode* node) { | |
| 871 | // Get entry of left side | ||
| 872 | 26703 | LLVMExprResult exprResult = this.visitExpr(lhsNode); | |
| 873 | 26703 | const SymbolTableEntry* entry = exprResult.entry; | |
| 874 | 26703 | llvm::Value lhsAddress; | |
| 875 | 26703 | if entry != nil<const SymbolTableEntry*> && isRefEntry(entry) { | |
| 876 | 1205 | lhsAddress = exprResult.refPtr; | |
| 877 | 1205 | } else { | |
| 878 | 25498 | lhsAddress = this.resolveAddress(exprResult); | |
| 879 | 25498 | } | |
| 880 | 26703 | return this.doAssignment(lhsAddress, entry, rhsNode, node); | |
| 881 | } | ||
| 882 | |||
| 883 | 59750 | #[ignoreUnusedReturnValue] | |
| 884 | 59750 | f<LLVMExprResult> IRGenerator.doAssignment(llvm::Value lhsAddress, const SymbolTableEntry* lhsEntry, IVisitable* rhsNode, ASTNode* node, bool isDecl = false) { | |
| 885 | // Get symbol type of right side | ||
| 886 | 59750 | const QualType rhsSType = this.getEvaluatedType(rhsNode); | |
| 887 | 59750 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 888 | 59750 | return this.doAssignment(lhsAddress, lhsEntry, rhs, rhsSType, node, isDecl); | |
| 889 | } | ||
| 890 | |||
| 891 | 60208 | #[ignoreUnusedReturnValue] | |
| 892 | 60208 | f<LLVMExprResult> IRGenerator.doAssignment(llvm::Value lhsAddress, const SymbolTableEntry* lhsEntry, LLVMExprResult& rhs, const QualType& rhsSType, ASTNode* node, bool isDecl) { | |
| 893 | // Deduce some information about the assignment | ||
| 894 | 60208 | const bool isRefAssign = lhsEntry != nil<const SymbolTableEntry*> && isRefEntry(lhsEntry); | |
| 895 | // A non-temporary struct value assigned by value needs a deep copy. This holds whether the destination is a direct | ||
| 896 | // lvalue or the value behind an already-bound reference (assign-through): the binding cases of a reference assignment | ||
| 897 | // (declaration/initial field ref/return value) all return early above before this is consumed, so the remaining | ||
| 898 | // reference assignments are assign-throughs that must copy into the referent instead of shallow-copying (which would | ||
| 899 | // alias the rhs' owned members and double-free). | ||
| 900 | // A reference rhs never denotes a temporary that could be stolen, even without an entry (e.g. a call returning a | ||
| 901 | // reference), because the referenced value is owned elsewhere. | ||
| 902 | 60208 | const QualType rhsSTypeNoRef = rhsSType.removeReferenceWrapper(); | |
| 903 | 60208 | const bool needsCopy = rhsSTypeNoRef.is(SuperType::TY_STRUCT) && (rhsSType.isRef() || !rhs.isTemporary()); | |
| 904 | |||
| 905 | 60208 | if isRefAssign { | |
| 906 | 4368 | assert lhsEntry != nil<const SymbolTableEntry*>; | |
| 907 | 4368 | if isDecl { // Reference gets initially assigned | |
| 908 | // Store lhs pointer to rhs | ||
| 909 | 3163 | const llvm::Value refAddress = this.insertAlloca(this.builder.getPtrTy()); | |
| 910 | 3163 | this.updateAddress(lhsEntry, refAddress); | |
| 911 | |||
| 912 | // Generate debug info for variable declaration | ||
| 913 | 3163 | this.diGenerator.generateLocalVarDebugInfo(lhsEntry.name, refAddress); | |
| 914 | |||
| 915 | // Get address of right side | ||
| 916 | 3163 | const llvm::Value rhsAddress = this.resolveAddress(rhs); | |
| 917 | 3163 | assert !rhsAddress.isNull(); | |
| 918 | 3163 | this.insertStore(rhsAddress, refAddress); | |
| 919 | |||
| 920 | 3163 | LLVMExprResult res; | |
| 921 | 3163 | res.value = rhsAddress; | |
| 922 | 3163 | res.ptr = refAddress; | |
| 923 | 3163 | res.entry = lhsEntry; | |
| 924 | 3163 | return res; | |
| 925 | } | ||
| 926 | |||
| 927 | // Reference to reference assignment (only for struct fields that are not initialized yet) | ||
| 928 | // These are only allowed inside a ctor body, for direct fields of 'this'. In other cases, the value of the reference gets | ||
| 929 | // assigned, not the ref itself. | ||
| 930 | 1205 | const bool isFieldOfThis = isAssignToDirectFieldOfThis(node); | |
| 931 | 1205 | const bool isInitialFieldRefAssign = this.isInCtorBody && isFieldOfThis && rhsSType.isRef() && rhs.entry != nil<const SymbolTableEntry*> && lhsEntry.isField(); | |
| 932 | // Assigning the result variable | ||
| 933 | 1205 | const bool isReturnValAssign = lhsEntry.name == RETURN_VARIABLE_NAME; | |
| 934 | 1205 | if isInitialFieldRefAssign || isReturnValAssign { | |
| 935 | // Get address of right side | ||
| 936 | 705 | const llvm::Value referencedAddress = this.resolveAddress(rhs); | |
| 937 | 705 | assert !referencedAddress.isNull(); | |
| 938 | |||
| 939 | // Store the rhs* to the lhs** | ||
| 940 | 705 | this.insertStore(referencedAddress, lhsAddress); | |
| 941 | |||
| 942 | 705 | LLVMExprResult res; | |
| 943 | 705 | res.value = referencedAddress; | |
| 944 | 705 | res.ptr = lhsAddress; | |
| 945 | 705 | res.entry = lhsEntry; | |
| 946 | 705 | return res; | |
| 947 | } | ||
| 948 | |||
| 949 | // Load referenced address | ||
| 950 | 500 | lhsAddress = this.insertLoad(this.builder.getPtrTy(), lhsAddress); | |
| 951 | 500 | } | |
| 952 | |||
| 953 | 56340 | if isDecl && rhsSType.is(SuperType::TY_STRUCT) && rhs.isTemporary() { | |
| 954 | 3285 | assert lhsEntry != nil<const SymbolTableEntry*>; | |
| 955 | 3285 | const llvm::Value rhsAddress = this.resolveAddress(rhs); | |
| 956 | // Only adopt the temporary's storage directly (temp stealing) unless it is a phi merging two independently | ||
| 957 | // materialized pointers (e.g. a ternary whose branches each own their own storage). Lifetime markers require | ||
| 958 | // their operand to be a real alloca, so stealing such a phi here would emit an invalid llvm.lifetime.end | ||
| 959 | // under --sanitizer=address. Any other pointer (a plain alloca, or one derived from it via GEP/load, as in | ||
| 960 | // the foreach-loop item extraction below) denotes a single, stable piece of storage and is safe to adopt. | ||
| 961 | // The temporary's ownership fully transfers to lhsEntry either way (its underlying storage is never | ||
| 962 | // separately destructed), so falling back to a shallow copy into a dedicated alloca is exactly as correct, | ||
| 963 | // just gives up the pointer-adoption optimization for the phi case. | ||
| 964 | 3285 | if !rhsAddress.isPHINode() { | |
| 965 | // Directly set the address to the lhs entry (temp stealing) | ||
| 966 | 3264 | this.updateAddress(lhsEntry, rhsAddress); | |
| 967 | 3264 | rhs.entry = lhsEntry; | |
| 968 | // Generate debug info for variable declaration | ||
| 969 | 3264 | this.diGenerator.generateLocalVarDebugInfo(lhsEntry.name, rhsAddress); | |
| 970 | 3264 | return rhs; | |
| 971 | } | ||
| 972 | // Size the copy off lhsEntry's own type, not rhsSType: some callers (e.g. the foreach-loop item | ||
| 973 | // extraction below) pass a struct rhsSType only to steer this branch, while rhs itself resolves (through | ||
| 974 | // refPtr indirection) to a differently-typed value; lhsEntry's type is always the authoritative one here. | ||
| 975 | 21 | const QualType& lhsQualType = lhsEntry.getQualType(); | |
| 976 | 21 | const llvm::Value lhsAddr = this.insertAlloca(lhsQualType); | |
| 977 | 21 | this.updateAddress(lhsEntry, lhsAddr); | |
| 978 | 21 | this.diGenerator.generateLocalVarDebugInfo(lhsEntry.name, lhsAddr); | |
| 979 | 21 | this.generateShallowCopy(rhsAddress, lhsQualType.toLLVMType(this.sourceFile), lhsAddr, lhsEntry.isVolatile); | |
| 980 | 21 | rhs.ptr = lhsAddr; | |
| 981 | 21 | rhs.entry = lhsEntry; | |
| 982 | 21 | return rhs; | |
| 983 | } | ||
| 984 | |||
| 985 | // Allocate new memory if the lhs address does not exist | ||
| 986 | 53055 | if lhsAddress.isNull() { | |
| 987 | 26983 | assert lhsEntry != nil<const SymbolTableEntry*>; | |
| 988 | 26983 | lhsAddress = this.insertAlloca(lhsEntry.getQualType()); | |
| 989 | 26983 | this.updateAddress(lhsEntry, lhsAddress); | |
| 990 | // Generate debug info for variable declaration | ||
| 991 | 26983 | this.diGenerator.generateLocalVarDebugInfo(lhsEntry.name, lhsAddress); | |
| 992 | 26983 | } | |
| 993 | |||
| 994 | // Check if we try to assign an array by value to a pointer. Here we have to store the address of the first element to the lhs | ||
| 995 | 53055 | if lhsEntry != nil<const SymbolTableEntry*> && isPtrEntry(lhsEntry) && rhsSType.isArray() && cast<unsigned long>(rhsSType.getArraySize()) != ARRAY_SIZE_UNKNOWN { | |
| 996 | // Get address of right side | ||
| 997 | 5 | const llvm::Value rhsAddress = this.resolveAddress(rhs); | |
| 998 | 5 | assert !rhsAddress.isNull(); | |
| 999 | 5 | const llvm::Type elementTy = rhsSType.toLLVMType(this.sourceFile); | |
| 1000 | 5 | Vector<llvm::Value> indices; | |
| 1001 | 5 | indices.pushBack(this.getInt64(0l)); | |
| 1002 | 5 | indices.pushBack(this.getInt32(0l)); | |
| 1003 | 5 | const llvm::Value firstItemAddress = this.insertInBoundsGEP(elementTy, rhsAddress, indices); | |
| 1004 | 5 | this.insertStore(firstItemAddress, lhsAddress); | |
| 1005 | 5 | LLVMExprResult res; | |
| 1006 | 5 | res.value = rhsAddress; | |
| 1007 | 5 | res.ptr = lhsAddress; | |
| 1008 | 5 | res.entry = lhsEntry; | |
| 1009 | 10 | return res; | |
| 1010 | 5 | } | |
| 1011 | |||
| 1012 | // Handle operator overloads | ||
| 1013 | 53050 | if !isDecl && this.conversionManager.callsOverloadedOpFct(node, DEFAULT_OP_IDX) { | |
| 1014 | 678 | const Resolver lhsV = noneResolver(); | |
| 1015 | 678 | const Resolver rhsV = valueResolver(rhsSType, &rhs); | |
| 1016 | 678 | const Resolver lhsP = fixedResolver(lhsAddress); | |
| 1017 | 678 | const Resolver rhsP = addressResolver(&rhs); | |
| 1018 | 678 | const OperandResolvers resolvers = OperandResolvers(lhsV, lhsP, rhsV, rhsP); | |
| 1019 | 1356 | return this.conversionManager.callOperatorOverloadFct(node, resolvers, 2l, DEFAULT_OP_IDX); | |
| 1020 | 678 | } | |
| 1021 | |||
| 1022 | // Check if we need to copy the rhs to the lhs. This happens for structs | ||
| 1023 | 52372 | if needsCopy { | |
| 1024 | // Get address of right side | ||
| 1025 | 715 | const llvm::Value rhsAddress = this.resolveAddress(rhs); | |
| 1026 | 715 | assert !rhsAddress.isNull(); | |
| 1027 | |||
| 1028 | // If the lhs already holds an initialized, non-trivially-destructible struct, its old value must be | ||
| 1029 | // destructed before the copy overwrites it, otherwise its owning members (heap pointers, strings, ...) | ||
| 1030 | // would leak. The typechecker only sets a dtor in exactly those cases. To stay correct for a self- | ||
| 1031 | // assignment like 'a = a', the destruct + copy are skipped entirely when both sides share the address | ||
| 1032 | // (the assignment is a no-op in that case, and destructing first would corrupt the value to copy from). | ||
| 1033 | 715 | const model::Function* lhsDtor = this.getLhsDtorFct(node); | |
| 1034 | 715 | llvm::BasicBlock bCopyEnd; | |
| 1035 | 715 | if lhsDtor != nil<const model::Function*> { | |
| 1036 | 26 | const llvm::BasicBlock bCopy = this.createBlock(String("assign.copy")); | |
| 1037 | 26 | bCopyEnd = this.createBlock(String("assign.copy.end")); | |
| 1038 | 26 | const llvm::Value selfAssign = this.builder.createICmp(llvm::LLVMIntPredicate::EQ, lhsAddress, rhsAddress); | |
| 1039 | 26 | this.insertCondJump(selfAssign, bCopyEnd, bCopy); | |
| 1040 | 26 | this.switchToBlock(bCopy); | |
| 1041 | 26 | const Vector<llvm::Value> noArgs; | |
| 1042 | 26 | this.generateCtorOrDtorCall(lhsAddress, lhsDtor, noArgs); | |
| 1043 | 26 | } | |
| 1044 | |||
| 1045 | 715 | const QualType rhsSTypeNonRef = rhsSTypeNoRef.toNonConst(); | |
| 1046 | 715 | if rhsSTypeNonRef.isTriviallyCopyable(node) { | |
| 1047 | // Create shallow copy | ||
| 1048 | 636 | const llvm::Type rhsType = rhsSTypeNonRef.toLLVMType(this.sourceFile); | |
| 1049 | 636 | this.generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry != nil<const SymbolTableEntry*> && lhsEntry.isVolatile); | |
| 1050 | 636 | } else { | |
| 1051 | // Check if we have a copy ctor | ||
| 1052 | 79 | Scope* structScope = rhsSTypeNonRef.getBodyScope(); | |
| 1053 | 79 | ArgList args; | |
| 1054 | 79 | args.pushBack(Pair<QualType, bool>(rhsSTypeNonRef.toConstRef(node), rhs.isTemporary())); | |
| 1055 | 79 | const model::Function* copyCtor = lookupFunction(structScope, String(CTOR_FUNCTION_NAME), rhsSTypeNonRef, args, true); | |
| 1056 | 79 | if copyCtor != nil<const model::Function*> { | |
| 1057 | // Call copy ctor | ||
| 1058 | 79 | Vector<llvm::Value> ctorArgs; | |
| 1059 | 79 | ctorArgs.pushBack(rhsAddress); | |
| 1060 | 79 | this.generateCtorOrDtorCall(lhsAddress, copyCtor, ctorArgs); | |
| 1061 | 79 | } else { | |
| 1062 | ✗ | const String structName = rhsSTypeNonRef.getName(); | |
| 1063 | ✗ | const String msg = "Cannot copy struct '" + structName + "', as it is not trivially copyable and has no copy ctor"; | |
| 1064 | ✗ | raiseSemanticError(node, SemanticErrorType::COPY_CTOR_REQUIRED, msg); | |
| 1065 | ✗ | } | |
| 1066 | 79 | } | |
| 1067 | |||
| 1068 | // Close the self-assignment guard | ||
| 1069 | 715 | if !bCopyEnd.isNull() { | |
| 1070 | 26 | this.insertJump(bCopyEnd); | |
| 1071 | 26 | this.switchToBlock(bCopyEnd); | |
| 1072 | 26 | } | |
| 1073 | 715 | LLVMExprResult res; | |
| 1074 | 715 | res.ptr = lhsAddress; | |
| 1075 | 715 | res.entry = lhsEntry; | |
| 1076 | 715 | return res; | |
| 1077 | } | ||
| 1078 | |||
| 1079 | // If a temporary struct is moved into an already initialized lhs (temp stealing), the old value of the lhs must be | ||
| 1080 | // destructed before it gets overwritten, otherwise its owning members would leak. The typechecker only sets a dtor in | ||
| 1081 | // exactly those cases. The rhs is already evaluated at this point and, being a temporary, never aliases the lhs. | ||
| 1082 | 51657 | if rhsSType.is(SuperType::TY_STRUCT) && rhs.isTemporary() { | |
| 1083 | 1254 | const model::Function* lhsDtor = this.getLhsDtorFct(node); | |
| 1084 | 1254 | if lhsDtor != nil<const model::Function*> { | |
| 1085 | 17 | const Vector<llvm::Value> noArgs; | |
| 1086 | 17 | this.generateCtorOrDtorCall(lhsAddress, lhsDtor, noArgs); | |
| 1087 | 17 | } | |
| 1088 | 1254 | } | |
| 1089 | |||
| 1090 | // Optimization: If we have the address of both sides, we can do a memcpy instead of loading and storing the value | ||
| 1091 | 51657 | llvm::Value rhsValue; | |
| 1092 | 51657 | if rhsSType.is(SuperType::TY_STRUCT) && rhs.value.isNull() && rhs.constant.isNull() { | |
| 1093 | // Create shallow copy | ||
| 1094 | 1240 | const QualType rhsSTypeNonRef = rhsSTypeNoRef.toNonConst(); | |
| 1095 | 1240 | const llvm::Type rhsType = rhsSTypeNonRef.toLLVMType(this.sourceFile); | |
| 1096 | 1240 | const llvm::Value rhsAddress = this.resolveAddress(rhs); | |
| 1097 | 1240 | assert !rhsAddress.isNull(); | |
| 1098 | 1240 | this.generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry != nil<const SymbolTableEntry*> && lhsEntry.isVolatile); | |
| 1099 | 1240 | } else { | |
| 1100 | // We can load the value from the right side and store it to the left side | ||
| 1101 | // Retrieve value of the right side | ||
| 1102 | 50417 | rhsValue = this.resolveValue(rhsSType, rhs); | |
| 1103 | // Store the value to the address | ||
| 1104 | 50417 | this.insertStore(rhsValue, lhsAddress, rhsSType); | |
| 1105 | 50417 | } | |
| 1106 | |||
| 1107 | 51657 | LLVMExprResult res; | |
| 1108 | 51657 | res.value = rhsValue; | |
| 1109 | 51657 | res.ptr = lhsAddress; | |
| 1110 | 51657 | res.entry = lhsEntry; | |
| 1111 | 51657 | return res; | |
| 1112 | } | ||
| 1113 | |||
| 1114 | // Check if the given node is an assignment to a direct field of 'this' | ||
| 1115 | 1205 | f<bool> isAssignToDirectFieldOfThis(const ASTNode* node) { | |
| 1116 | 1205 | if node.kind != ASTNodeKind::ASSIGN_EXPR { | |
| 1117 | ✗ | return false; | |
| 1118 | } | ||
| 1119 | 1205 | const ASTAssignExprNode* assignNode = downcast<ASTAssignExprNode>(node); | |
| 1120 | 1205 | return isDirectFieldOfThis(getASTNode(assignNode.lhs)); | |
| 1121 | } | ||
| 1122 | |||
| 1123 | // Check if the type of the given entry is a reference | ||
| 1124 | 79421 | f<bool> isRefEntry(const SymbolTableEntry* entry) { | |
| 1125 | 79421 | const QualType& qualType = entry.getQualType(); | |
| 1126 | 79421 | return qualType.isRef(); | |
| 1127 | } | ||
| 1128 | |||
| 1129 | // Check if the type of the given entry is a pointer | ||
| 1130 | 49310 | f<bool> isPtrEntry(const SymbolTableEntry* entry) { | |
| 1131 | 49310 | const QualType& qualType = entry.getQualType(); | |
| 1132 | 49310 | return qualType.isPtr(); | |
| 1133 | } | ||
| 1134 | |||
| 1135 | // Get the dtor of the lhs of an assignment, if the lhs gets overwritten | ||
| 1136 | 1969 | f<const model::Function*> IRGenerator.getLhsDtorFct(ASTNode* node) { | |
| 1137 | 1969 | if node.kind != ASTNodeKind::ASSIGN_EXPR { | |
| 1138 | 108 | return nil<const model::Function*>; | |
| 1139 | } | ||
| 1140 | 1861 | const ASTAssignExprNode* assignNode = downcast<ASTAssignExprNode>(node); | |
| 1141 | 1861 | return assignNode.lhsDtorFct.get(this.manIdx); | |
| 1142 | } | ||
| 1143 | |||
| 1144 | 2136 | p IRGenerator.generateShallowCopy(llvm::Value oldAddress, llvm::Type varType, llvm::Value targetAddress, bool isVolatile) { | |
| 1145 | // Retrieve size to copy | ||
| 1146 | 2136 | const unsigned long typeSize = this.module.getTypeAllocSize(varType); | |
| 1147 | |||
| 1148 | // Create values for memcpy intrinsic | ||
| 1149 | 2136 | const llvm::Value structSize = this.builder.getInt64(typeSize); | |
| 1150 | 2136 | const llvm::Value copyVolatile = this.getInt1(isVolatile); | |
| 1151 | |||
| 1152 | // Call memcpy intrinsic to execute the shallow copy | ||
| 1153 | 2136 | const llvm::Function memcpyFct = this.stdFunctionManager.getMemcpyIntrinsic(); | |
| 1154 | 2136 | assert !targetAddress.isNull(); | |
| 1155 | 2136 | Vector<llvm::Value> args; | |
| 1156 | 2136 | args.pushBack(targetAddress); | |
| 1157 | 2136 | args.pushBack(oldAddress); | |
| 1158 | 2136 | args.pushBack(structSize); | |
| 1159 | 2136 | args.pushBack(copyVolatile); | |
| 1160 | 2136 | this.builder.createCall(memcpyFct, args); | |
| 1161 | 2136 | } | |
| 1162 | |||
| 1163 | 112163 | p IRGenerator.autoDeReferencePtr(llvm::Value& ptr, QualType& symbolType) { | |
| 1164 | 197912 | while symbolType.isPtr() || symbolType.isRef() { | |
| 1165 | 85749 | ptr = this.insertLoad(symbolType, ptr); | |
| 1166 | 85749 | symbolType = symbolType.getContained(); | |
| 1167 | 85749 | } | |
| 1168 | 112163 | } | |
| 1169 | |||
| 1170 | 152 | f<llvm::Value> IRGenerator.createGlobalConst(const String& baseName, llvm::Value constant) { | |
| 1171 | // Get unused name | ||
| 1172 | 152 | const String globalName = this.getUnusedGlobalName(baseName); | |
| 1173 | // Create global | ||
| 1174 | 152 | llvm::Value global = this.module.getOrInsertGlobal(globalName.getRaw(), constant.getType()); | |
| 1175 | // Set initializer to the given constant | ||
| 1176 | 152 | global.setInitializer(constant); | |
| 1177 | 152 | global.setConstant(true); | |
| 1178 | 152 | global.setLinkage(llvm::LLVMLinkage::PrivateLinkage); | |
| 1179 | 152 | global.setUnnamedAddr(llvm::LLVMUnnamedAddr::GlobalUnnamedAddr); | |
| 1180 | 304 | return global; | |
| 1181 | 152 | } | |
| 1182 | |||
| 1183 | 13426 | f<llvm::Value> IRGenerator.createGlobalStringConst(const String& baseName, const String& value) { | |
| 1184 | // Get unused name | ||
| 1185 | 13426 | const String globalName = this.getUnusedGlobalName(baseName); | |
| 1186 | // Create global | ||
| 1187 | 13426 | llvm::Value global = this.module.createGlobalString(value, globalName.getRaw()); | |
| 1188 | // If the output should be comparable, fix alignment to 4 bytes | ||
| 1189 | 13426 | if this.cliOptions.comparableOutput { | |
| 1190 | 13426 | global.setAlignment(4); | |
| 1191 | 13426 | } | |
| 1192 | 26852 | return global; | |
| 1193 | 13426 | } | |
| 1194 | |||
| 1195 | 13426 | f<llvm::Value> IRGenerator.createGlobalStringConst(const String& baseName, const String& value, const CodeLoc& codeLoc) { | |
| 1196 | 13426 | const llvm::Value global = this.createGlobalStringConst(baseName, value); | |
| 1197 | // Create debug info | ||
| 1198 | 13426 | if this.cliOptions.instrumentation.emitsFullDebugInfo() { | |
| 1199 | 250 | this.diGenerator.generateGlobalStringDebugInfo(global, global.getName(), value.getLength(), codeLoc); | |
| 1200 | 250 | } | |
| 1201 | 13426 | return global; | |
| 1202 | } | ||
| 1203 | |||
| 1204 | 21333 | f<String> IRGenerator.getUnusedGlobalName(const String& baseName) { | |
| 1205 | // Find an unused global name | ||
| 1206 | 21333 | String globalName; | |
| 1207 | 21333 | unsigned int suffixNumber = 0; | |
| 1208 | 21333 | do { | |
| 1209 | 515283 | globalName = baseName + toString(suffixNumber); | |
| 1210 | 515283 | suffixNumber++; | |
| 1211 | 515283 | const llvm::Value existingGlobal = this.module.getNamedGlobal(globalName.getRaw()); | |
| 1212 | 515283 | if existingGlobal.isNull() { | |
| 1213 | 21333 | break; | |
| 1214 | } | ||
| 1215 | 515283 | } while true; | |
| 1216 | 21333 | return globalName; | |
| 1217 | } | ||
| 1218 | |||
| 1219 | 86107 | p materializeConstant(LLVMExprResult& exprResult) { | |
| 1220 | // Skip results, that do not contain a constant or already have a value | ||
| 1221 | 86107 | if !exprResult.value.isNull() || exprResult.constant.isNull() { | |
| 1222 | 217 | return; | |
| 1223 | } | ||
| 1224 | |||
| 1225 | // Default case: the value to the constant | ||
| 1226 | 85890 | exprResult.value = exprResult.constant; | |
| 1227 | 85890 | } | |
| 1228 | |||
| 1229 | 48043 | f<llvm::LLVMLinkage> IRGenerator.getSymbolLinkageType(bool isPublic) { | |
| 1230 | 48043 | return isPublic ? llvm::LLVMLinkage::ExternalLinkage : llvm::LLVMLinkage::InternalLinkage; | |
| 1231 | } | ||
| 1232 | |||
| 1233 | 4662 | f<llvm::LLVMLinkage> IRGenerator.getVTableLinkageType(bool isPublic) { | |
| 1234 | // VTables, type infos and type info names are ODR entities that may legitimately be emitted in more than one | ||
| 1235 | // translation unit (e.g. an interface that is defined in one file and used from several importing files). Giving | ||
| 1236 | // them weak ODR linkage lets the linker coalesce the duplicates. On ELF this pairs with the comdat group below; on | ||
| 1237 | // MachO, which has no comdat support, the weak/coalesced linkage is what prevents a duplicate-symbol error. | ||
| 1238 | 4662 | return isPublic ? llvm::LLVMLinkage::WeakODRLinkage : llvm::LLVMLinkage::PrivateLinkage; | |
| 1239 | } | ||
| 1240 | |||
| 1241 | 4662 | p IRGenerator.attachComdatToSymbol(llvm::Value global, const String& comdatName, bool isPublic) { | |
| 1242 | // MachO does not support comdat annotations | ||
| 1243 | 4662 | if isPublic && !this.cliOptions.targetTriple.isMachO() { | |
| 1244 | 4440 | global.setComdat(this.module.getOrInsertComdat(comdatName.getRaw())); | |
| 1245 | 4440 | } | |
| 1246 | 4662 | } | |
| 1247 | |||
| 1248 | /** | ||
| 1249 | * Attach the function attributes that all functions we emit have in common. | ||
| 1250 | * | ||
| 1251 | * Spice does not know exceptions and we never emit landing pads, so none of our functions can unwind. We still request | ||
| 1252 | * an unwind table, so that debuggers and profilers are able to produce correct stack traces. | ||
| 1253 | * | ||
| 1254 | * Frame pointers are off by default and are requested per function by '--keep-frame-pointers'. This attribute is the | ||
| 1255 | * only thing the backend reads. The 'frame-pointer' module flag that IRGenerator's constructor sets from the same | ||
| 1256 | * option covers a different set of functions - Function::createWithDefaultAttr() stamps it onto the functions LLVM | ||
| 1257 | * itself synthesizes, such as the sanitizer module ctors - so both have to be set, and the module flag alone changes | ||
| 1258 | * nothing about the functions we emit: x86-64 Linux then omits the frame pointer at every optimization level, and | ||
| 1259 | * AArch64 spills the frame record but never links the chain up. | ||
| 1260 | * | ||
| 1261 | * Nothing is emitted in the default case, rather than an explicit "none", which is exactly what LLVM does for its own | ||
| 1262 | * synthesized functions. It also leaves the decision to the backend, so targets whose ABI mandates a frame pointer | ||
| 1263 | * (e.g. AArch64 on Darwin) keep theirs. | ||
| 1264 | * | ||
| 1265 | * '--no-red-zone' adds 'noredzone' to every function, like Clang's '-mno-red-zone'. Leaf functions then allocate their | ||
| 1266 | * stack frame instead of using the area below the stack pointer, which the x86_64 SysV ABI reserves for them. Kernel | ||
| 1267 | * code needs this, because an interrupt without a stack switch pushes its frame onto the current stack. Targets without | ||
| 1268 | * a red zone, like AArch64, are not affected. Functions and procedures can override the option with the | ||
| 1269 | * 'core.compiler.redZone' attribute. | ||
| 1270 | * | ||
| 1271 | * The size levels are not communicated to LLVM by the pass pipeline alone - since Os and Oz both select the O2 | ||
| 1272 | * pipeline, 'optsize' and 'minsize' on the individual function are what actually distinguishes them. Without 'minsize', | ||
| 1273 | * Oz is indistinguishable from Os. | ||
| 1274 | * | ||
| 1275 | * @param fct Function to attach the attributes to | ||
| 1276 | * @param isAlwaysInline Whether the function was declared as inline | ||
| 1277 | * @param attrs Attributes of the function or procedure definition, if any | ||
| 1278 | */ | ||
| 1279 | 44787 | p IRGenerator.addCommonFctAttrs(llvm::Function fct, bool isAlwaysInline = false, IVisitable* attrs = nil<IVisitable*>) { | |
| 1280 | 44787 | fct.addFnAttr("nounwind"); | |
| 1281 | 44787 | fct.addFnAttr("uwtable", 2l); // llvm::UWTableKind::Default | |
| 1282 | 44787 | if this.cliOptions.keepFramePointers { | |
| 1283 | 4 | fct.addFnStringAttr("frame-pointer", "all"); | |
| 1284 | 4 | } | |
| 1285 | 44787 | if this.isRedZoneDisabled(attrs) { | |
| 1286 | 10 | fct.addFnAttr("noredzone"); | |
| 1287 | 10 | } | |
| 1288 | |||
| 1289 | // Explicitly inlined functions must not be marked as 'optnone', because that is incompatible with 'alwaysinline'. | ||
| 1290 | // This matches the behavior of other frontends: an inline request is honored, even at O0. | ||
| 1291 | 44787 | if isAlwaysInline { | |
| 1292 | 11445 | fct.addFnAttr("alwaysinline"); | |
| 1293 | 44787 | } else if this.cliOptions.optLevel == OptLevel::O0 { | |
| 1294 | 32892 | fct.addFnAttr("optnone"); | |
| 1295 | 32892 | fct.addFnAttr("noinline"); // 'optnone' requires 'noinline' | |
| 1296 | 33342 | } | |
| 1297 | |||
| 1298 | 44787 | if this.cliOptions.optLevel >= OptLevel::Os { | |
| 1299 | 3 | fct.addFnAttr("optsize"); | |
| 1300 | 3 | if this.cliOptions.optLevel == OptLevel::Oz { | |
| 1301 | 2 | fct.addFnAttr("minsize"); | |
| 1302 | 2 | } | |
| 1303 | 3 | } | |
| 1304 | 44787 | } | |
| 1305 | |||
| 1306 | /** | ||
| 1307 | * Check whether a function must not use the red zone: with '--no-red-zone', unless the function allows it with the | ||
| 1308 | * 'core.compiler.redZone' attribute, or without the option, if the attribute forbids it | ||
| 1309 | * | ||
| 1310 | * @param attrs Attributes of the function or procedure definition, if any | ||
| 1311 | * @return Whether the function gets the 'noredzone' attribute | ||
| 1312 | */ | ||
| 1313 | 44787 | f<bool> IRGenerator.isRedZoneDisabled(IVisitable* attrs) { | |
| 1314 | 44787 | if attrs != nil<IVisitable*> { | |
| 1315 | 1208 | const ASTTopLevelDefAttrNode* topLevelDefAttrs = downcast<ASTTopLevelDefAttrNode>(attrs); | |
| 1316 | 1208 | const String redZoneAttrName = String(ATTR_CORE_COMPILER_RED_ZONE); | |
| 1317 | 1208 | if topLevelDefAttrs.attrLst.hasAttr(redZoneAttrName) { | |
| 1318 | 4 | const CompileTimeValue redZoneAttrValue = topLevelDefAttrs.attrLst.getAttrValueByName(redZoneAttrName); | |
| 1319 | 8 | return !redZoneAttrValue.getBoolValue(); | |
| 1320 | } | ||
| 1321 | 1208 | } | |
| 1322 | 44783 | return this.cliOptions.noRedZone; | |
| 1323 | } | ||
| 1324 | |||
| 1325 | 311597 | public f<llvm::Value> IRGenerator.getAddress(const SymbolTableEntry* entry) { | |
| 1326 | 311597 | const long key = getEntryKey(entry); | |
| 1327 | 311597 | if !this.addressMap.contains(key) { | |
| 1328 | 1420 | return llvm::Value(); | |
| 1329 | } | ||
| 1330 | 310177 | Stack<llvm::Value>& stack = this.addressMap.get(key); | |
| 1331 | 310177 | if stack.isEmpty() { | |
| 1332 | 3 | return llvm::Value(); | |
| 1333 | } | ||
| 1334 | 310174 | return stack.top(); | |
| 1335 | } | ||
| 1336 | |||
| 1337 | 195389 | public p IRGenerator.updateAddress(const SymbolTableEntry* entry, llvm::Value address) { | |
| 1338 | 195389 | assert !address.isNull(); | |
| 1339 | 195389 | const llvm::Type addressType = address.getType(); | |
| 1340 | 195389 | assert addressType.isPointerTy(); | |
| 1341 | 195389 | const long key = getEntryKey(entry); | |
| 1342 | 195389 | if !this.addressMap.contains(key) { | |
| 1343 | 153783 | Stack<llvm::Value> newStack; | |
| 1344 | 153783 | this.addressMap.upsert(key, newStack); | |
| 1345 | 153783 | } | |
| 1346 | 195389 | Stack<llvm::Value>& stack = this.addressMap.get(key); | |
| 1347 | 195389 | if !stack.isEmpty() { | |
| 1348 | 41606 | stack.pop(); | |
| 1349 | 41606 | } | |
| 1350 | 195389 | stack.push(address); | |
| 1351 | 195389 | } | |
| 1352 | |||
| 1353 | 92 | public p IRGenerator.pushAddress(const SymbolTableEntry* entry, llvm::Value address) { | |
| 1354 | 92 | assert !address.isNull(); | |
| 1355 | 92 | const long key = getEntryKey(entry); | |
| 1356 | 92 | if !this.addressMap.contains(key) { | |
| 1357 | 3 | Stack<llvm::Value> newStack; | |
| 1358 | 3 | this.addressMap.upsert(key, newStack); | |
| 1359 | 3 | } | |
| 1360 | 92 | Stack<llvm::Value>& stack = this.addressMap.get(key); | |
| 1361 | 92 | stack.push(address); | |
| 1362 | 92 | } | |
| 1363 | |||
| 1364 | 92 | public p IRGenerator.popAddress(const SymbolTableEntry* entry) { | |
| 1365 | 92 | const long key = getEntryKey(entry); | |
| 1366 | 92 | assert this.addressMap.contains(key); | |
| 1367 | 92 | Stack<llvm::Value>& stack = this.addressMap.get(key); | |
| 1368 | 92 | assert !stack.isEmpty(); | |
| 1369 | 92 | stack.pop(); | |
| 1370 | 92 | } | |
| 1371 | |||
| 1372 | 3511 | public f<llvm::Function> IRGenerator.getLLVMFunction(const model::Function* spiceFunc) { | |
| 1373 | 3511 | const long key = getFunctionKey(spiceFunc); | |
| 1374 | 3511 | if !this.llvmFunctions.contains(key) { | |
| 1375 | 1755 | return llvm::Function(); | |
| 1376 | } | ||
| 1377 | 1756 | return this.llvmFunctions.get(key); | |
| 1378 | } | ||
| 1379 | |||
| 1380 | 43130 | public p IRGenerator.setLLVMFunction(const model::Function* spiceFunc, llvm::Function llvmFunction) { | |
| 1381 | 43130 | assert !llvmFunction.isNull(); | |
| 1382 | 43130 | this.llvmFunctions.upsert(getFunctionKey(spiceFunc), llvmFunction); | |
| 1383 | 43130 | } | |
| 1384 | |||
| 1385 | /** | ||
| 1386 | * Get the information, how a value of the given type is returned from a function on the current target | ||
| 1387 | * | ||
| 1388 | * @param returnType Return type of the function, dyn for procedures | ||
| 1389 | * @return Return ABI info | ||
| 1390 | */ | ||
| 1391 | 339382 | public f<ReturnABIInfo> IRGenerator.getReturnABIInfo(const QualType& returnType) { | |
| 1392 | 339382 | if returnType.is(SuperType::TY_DYN) { | |
| 1393 | 99787 | return ReturnABIInfo{ReturnABIKind::DIRECT, this.builder.getVoidTy(), llvm::Type()}; | |
| 1394 | } | ||
| 1395 | 239595 | llvm::Type llvmType = returnType.toLLVMType(this.sourceFile); | |
| 1396 | // Types with a copy ctor or dtor are non-trivial for the purpose of calls and therefore returned via memory | ||
| 1397 | 239595 | const bool isNonTrivial = llvmType.isAggregateType() && | |
| 1398 | 239595 | (!returnType.isTriviallyCopyable(nil<const ASTNode*>) || !returnType.isTriviallyDestructible(nil<const ASTNode*>)); | |
| 1399 | 239595 | return this.abiInfo.classifyReturnType(llvmType, isNonTrivial); | |
| 1400 | } | ||
| 1401 | |||
| 1402 | /** | ||
| 1403 | * Get the LLVM function type for a function with the given return and param types, with the return value lowered to the | ||
| 1404 | * calling convention of the target. If the value is returned via memory, the sret pointer is the first parameter. | ||
| 1405 | * | ||
| 1406 | * @param returnType Return type of the function, dyn for procedures | ||
| 1407 | * @param paramTypes LLVM types of the parameters, including the 'this' pointer for methods | ||
| 1408 | * @param isVarArg Variadic function or not | ||
| 1409 | * @return Lowered LLVM function type | ||
| 1410 | */ | ||
| 1411 | 63125 | public f<llvm::Type> IRGenerator.getFunctionType(const QualType& returnType, const Vector<llvm::Type>& paramTypes, bool isVarArg = false) { | |
| 1412 | 63125 | const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType); | |
| 1413 | 63125 | if returnABI.isCoerced() { | |
| 1414 | 1163 | return llvm::getFunctionType(returnABI.coercedType, paramTypes, isVarArg); | |
| 1415 | } | ||
| 1416 | 61962 | if returnABI.isIndirect() { | |
| 1417 | 5822 | Vector<llvm::Type> loweredParamTypes; | |
| 1418 | 5822 | loweredParamTypes.pushBack(this.builder.getPtrTy()); | |
| 1419 | 13613 | foreach const llvm::Type& paramType : paramTypes { | |
| 1420 | 7791 | loweredParamTypes.pushBack(paramType); | |
| 1421 | 7791 | } | |
| 1422 | 11644 | return llvm::getFunctionType(this.builder.getVoidTy(), loweredParamTypes, isVarArg); | |
| 1423 | 5822 | } | |
| 1424 | 56140 | return llvm::getFunctionType(returnABI.memoryType, paramTypes, isVarArg); | |
| 1425 | } | ||
| 1426 | |||
| 1427 | /** | ||
| 1428 | * Get the index of the first actual parameter of a function with the given return type. It is shifted by one, if the | ||
| 1429 | * function has an sret parameter. | ||
| 1430 | * | ||
| 1431 | * @param returnType Return type of the function, dyn for procedures | ||
| 1432 | * @return Index of the first actual parameter | ||
| 1433 | */ | ||
| 1434 | 115598 | public f<unsigned int> IRGenerator.getArgOffset(const QualType& returnType) { | |
| 1435 | 115598 | const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType); | |
| 1436 | 115598 | return returnABI.isIndirect() ? 1 : 0; | |
| 1437 | } | ||
| 1438 | |||
| 1439 | /** | ||
| 1440 | * Insert a call to a function, that returns a value of the given type, lowered to the calling convention of the target. | ||
| 1441 | * If the value is returned via memory or coerced, its address is returned via resultAddr. Otherwise, resultAddr is | ||
| 1442 | * set to a null value and the call itself is the returned value. | ||
| 1443 | * | ||
| 1444 | * @param fctType Lowered function type of the callee | ||
| 1445 | * @param callee Callee | ||
| 1446 | * @param args Arguments, not including the sret pointer | ||
| 1447 | * @param returnType Return type of the callee, dyn for procedures | ||
| 1448 | * @param resultAddr Address of the returned value, if it is returned via memory or coerced | ||
| 1449 | * @param sretAddr Memory to return the value to, if it is returned via memory. A new temporary is used, if null | ||
| 1450 | * @return Call instruction | ||
| 1451 | */ | ||
| 1452 | 72909 | public f<llvm::Value> IRGenerator.insertCall(llvm::Type fctType, llvm::Value callee, const Vector<llvm::Value>& args, | |
| 1453 | 72909 | const QualType& returnType, llvm::Value& resultAddr, llvm::Value sretAddr = llvm::Value()) { | |
| 1454 | 72909 | const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType); | |
| 1455 | 72909 | resultAddr = llvm::Value(); | |
| 1456 | 72909 | Vector<llvm::Value> loweredArgs; | |
| 1457 | 72909 | if returnABI.isIndirect() { | |
| 1458 | 5318 | resultAddr = sretAddr.isNull() ? this.insertAlloca(returnABI.memoryType) : sretAddr; | |
| 1459 | 5318 | loweredArgs.pushBack(resultAddr); | |
| 1460 | 5318 | } | |
| 1461 | 205618 | foreach const llvm::Value& arg : args { | |
| 1462 | 132709 | loweredArgs.pushBack(arg); | |
| 1463 | 132709 | } | |
| 1464 | |||
| 1465 | 72909 | llvm::Value callInst = this.builder.createCall(fctType, callee, loweredArgs); | |
| 1466 | |||
| 1467 | 72909 | if returnABI.isIndirect() { | |
| 1468 | 5318 | this.addSRetCallParamAttrs(callInst, returnABI.memoryType); | |
| 1469 | 72909 | } else if returnABI.isCoerced() { | |
| 1470 | // Store the coerced value to memory, which can be accessed as the actual return type | ||
| 1471 | 246 | resultAddr = this.insertCoercionAlloca(returnABI.memoryType, returnABI.coercedType); | |
| 1472 | 246 | this.insertStore(callInst, resultAddr); | |
| 1473 | 67591 | } | |
| 1474 | 145818 | return callInst; | |
| 1475 | 72909 | } | |
| 1476 | |||
| 1477 | /** | ||
| 1478 | * Insert a call to the given function, that returns a value of the given type (see the overload above) | ||
| 1479 | */ | ||
| 1480 | 5430 | public f<llvm::Value> IRGenerator.insertCall(llvm::Function callee, const Vector<llvm::Value>& args, const QualType& returnType, | |
| 1481 | 5430 | llvm::Value& resultAddr, llvm::Value sretAddr = llvm::Value()) { | |
| 1482 | 5430 | return this.insertCall(callee.getFunctionType(), callee.asValue(), args, returnType, resultAddr, sretAddr); | |
| 1483 | } | ||
| 1484 | |||
| 1485 | /** | ||
| 1486 | * Insert a return instruction for the current function, lowered to the calling convention of the target | ||
| 1487 | * | ||
| 1488 | * @param returnValue Value to return or a null value. For functions with an sret parameter, a null value means, that the | ||
| 1489 | * value was already stored to the sret memory | ||
| 1490 | */ | ||
| 1491 | 42093 | public p IRGenerator.insertReturn(llvm::Value returnValue) { | |
| 1492 | 42093 | const llvm::BasicBlock currentBlock = this.builder.getInsertBlock(); | |
| 1493 | 42093 | llvm::Function function = currentBlock.getParent(); | |
| 1494 | |||
| 1495 | // Store the value to the memory of the caller | ||
| 1496 | 42093 | const llvm::Value sretArg = getSRetArg(function); | |
| 1497 | 42093 | if !sretArg.isNull() { | |
| 1498 | 5319 | if !returnValue.isNull() { | |
| 1499 | 4402 | this.insertStore(returnValue, sretArg); | |
| 1500 | 4402 | } | |
| 1501 | 5319 | this.builder.createRetVoid(); | |
| 1502 | 5319 | return; | |
| 1503 | } | ||
| 1504 | |||
| 1505 | 36774 | if returnValue.isNull() { | |
| 1506 | 4077 | this.builder.createRetVoid(); | |
| 1507 | 4077 | return; | |
| 1508 | } | ||
| 1509 | |||
| 1510 | // Coerce the value to the return type via memory | ||
| 1511 | 32697 | const llvm::Type returnType = function.getReturnType(); | |
| 1512 | 32697 | const llvm::Type valueType = returnValue.getType(); | |
| 1513 | 32697 | if !valueType.isSameAs(returnType) { | |
| 1514 | 1139 | const llvm::Value coercionAddr = this.insertCoercionAlloca(valueType, returnType); | |
| 1515 | 1139 | this.insertStore(returnValue, coercionAddr); | |
| 1516 | 1139 | returnValue = this.insertLoad(returnType, coercionAddr); | |
| 1517 | 1139 | } | |
| 1518 | 32697 | this.builder.createRet(returnValue); | |
| 1519 | 32697 | } | |
| 1520 | |||
| 1521 | /** | ||
| 1522 | * Allocate memory, that can hold a value of both given types | ||
| 1523 | * | ||
| 1524 | * @param llvmType Actual type of the value | ||
| 1525 | * @param coercedType Type, the value is coerced to | ||
| 1526 | * @return Memory address | ||
| 1527 | */ | ||
| 1528 | 1385 | f<llvm::Value> IRGenerator.insertCoercionAlloca(llvm::Type llvmType, llvm::Type coercedType) { | |
| 1529 | 1385 | const bool isCoercedTypeLarger = this.module.getTypeAllocSize(coercedType) > this.module.getTypeAllocSize(llvmType); | |
| 1530 | 1385 | llvm::Value alloca = this.insertAlloca(isCoercedTypeLarger ? coercedType : llvmType); | |
| 1531 | 1385 | const unsigned int typeAlign = this.module.getPrefTypeAlign(llvmType); | |
| 1532 | 1385 | const unsigned int coercedTypeAlign = this.module.getPrefTypeAlign(coercedType); | |
| 1533 | 1385 | alloca.setAlignment(typeAlign > coercedTypeAlign ? typeAlign : coercedTypeAlign); | |
| 1534 | 1385 | return alloca; | |
| 1535 | } | ||
| 1536 | |||
| 1537 | /** | ||
| 1538 | * Get the sret parameter of the given function | ||
| 1539 | * | ||
| 1540 | * @param function LLVM function | ||
| 1541 | * @return sret parameter or a null value, if the function does not return via memory | ||
| 1542 | */ | ||
| 1543 | 85299 | public f<llvm::Value> getSRetArg(llvm::Function function) { | |
| 1544 | 85299 | if !function.hasParamAttr(0, "sret") { | |
| 1545 | 74207 | return llvm::Value(); | |
| 1546 | } | ||
| 1547 | 11092 | return function.getArg(0); | |
| 1548 | } | ||
| 1549 | |||
| 1550 | 5814 | public p IRGenerator.addSRetParamAttrs(llvm::Function function, llvm::Type llvmType) { | |
| 1551 | 5814 | function.addParamAttr(0, "dead_on_unwind"); | |
| 1552 | 5814 | function.addParamAttr(0, "noalias"); | |
| 1553 | 5814 | function.addParamAttr(0, "writable"); | |
| 1554 | 5814 | function.addParamTypeAttr(0, "sret", llvmType); | |
| 1555 | 5814 | function.addParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(llvmType))); | |
| 1556 | 5814 | } | |
| 1557 | |||
| 1558 | 5319 | public p IRGenerator.addSRetCallParamAttrs(llvm::Value callInst, llvm::Type llvmType) { | |
| 1559 | 5319 | callInst.addCallParamAttr(0, "dead_on_unwind"); | |
| 1560 | 5319 | callInst.addCallParamAttr(0, "writable"); | |
| 1561 | 5319 | callInst.addCallParamTypeAttr(0, "sret", llvmType); | |
| 1562 | 5319 | callInst.addCallParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(llvmType))); | |
| 1563 | 5319 | } | |
| 1564 | |||
| 1565 | /** | ||
| 1566 | * Print the given module to a string | ||
| 1567 | * | ||
| 1568 | * @param llvmModule LLVM module | ||
| 1569 | * @param cliOptions Cli options | ||
| 1570 | * @return IR string | ||
| 1571 | */ | ||
| 1572 | 7000 | public f<String> getIRString(llvm::Module* llvmModule, const CliOptions& cliOptions) { | |
| 1573 | 7000 | const bool eliminateTarget = cliOptions.comparableOutput && cliOptions.isNativeTarget; | |
| 1574 | |||
| 1575 | // Backup target triple and data layout | ||
| 1576 | 7000 | const String targetTriple = llvmModule.getTargetTripleString(); | |
| 1577 | 7000 | const String targetDataLayout = llvmModule.getDataLayoutString(); | |
| 1578 | // Remove target triple and data layout | ||
| 1579 | 7000 | if eliminateTarget { | |
| 1580 | 6776 | llvmModule.setTargetTripleString(""); | |
| 1581 | 6776 | llvmModule.setDataLayoutString(""); | |
| 1582 | 6776 | } | |
| 1583 | |||
| 1584 | // Get IR string | ||
| 1585 | 7000 | const String output = llvmModule.printToString(); | |
| 1586 | |||
| 1587 | // Restore target triple and data layout | ||
| 1588 | 7000 | if eliminateTarget { | |
| 1589 | 6776 | llvmModule.setTargetTripleString(targetTriple.getRaw()); | |
| 1590 | 6776 | llvmModule.setDataLayoutString(targetDataLayout.getRaw()); | |
| 1591 | 6776 | } | |
| 1592 | |||
| 1593 | 14000 | return output; | |
| 1594 | 7000 | } | |
| 1595 | |||
| 1596 | /** | ||
| 1597 | * Returns the operator function list for the current manifestation and the given node | ||
| 1598 | * | ||
| 1599 | * @param node Node to retrieve the op fct pointer list from | ||
| 1600 | * @return Op fct pointer list | ||
| 1601 | */ | ||
| 1602 | 124696 | public f<const Vector<const model::Function*>&> IRGenerator.getOpFctPointers(ASTNode* node) { | |
| 1603 | 124696 | const Vector<Vector<const model::Function*>>* opFctPointers = node.getOpFctPointers(); | |
| 1604 | 124696 | assert opFctPointers.getSize() > this.manIdx; | |
| 1605 | 124696 | return opFctPointers.get(this.manIdx); | |
| 1606 | } | ||
| 1607 | |||
| 1608 | // ==================================================== Top level definitions ==================================================== | ||
| 1609 | |||
| 1610 | 751 | public f<Any> IRGenerator.visitMainFctDef(ASTMainFctDefNode* node) { | |
| 1611 | // Ignore main function definitions if this is not the main source file | ||
| 1612 | 751 | if !this.sourceFile.isMainFile { | |
| 1613 | ✗ | return Any(); | |
| 1614 | } | ||
| 1615 | |||
| 1616 | // Do not generate main function if it is explicitly specified | ||
| 1617 | 751 | if this.cliOptions.noEntryFct { | |
| 1618 | 3 | return Any(); | |
| 1619 | } | ||
| 1620 | |||
| 1621 | // Change scope to function scope | ||
| 1622 | 748 | this.currentScope = node.bodyScope; | |
| 1623 | 748 | assert this.currentScope != nil<Scope*>; | |
| 1624 | |||
| 1625 | // Visit parameters | ||
| 1626 | 748 | ParamInfoList paramInfoList; | |
| 1627 | 748 | QualTypeList paramSymbolTypes; | |
| 1628 | 748 | Vector<llvm::Type> paramTypes; | |
| 1629 | 748 | if node.takesArgs { | |
| 1630 | 7 | const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(node.paramLst); | |
| 1631 | 7 | const unsigned long numOfParams = paramLst.params.getSize(); | |
| 1632 | 7 | paramInfoList.reserve(numOfParams); | |
| 1633 | 7 | paramSymbolTypes.reserve(numOfParams); | |
| 1634 | 7 | paramTypes.reserve(numOfParams); | |
| 1635 | 21 | foreach ASTDeclStmtNode* param : paramLst.params { | |
| 1636 | // Get symbol table entry of param | ||
| 1637 | 14 | const SymbolTableEntry* paramSymbol = node.bodyScope.lookupStrict(param.varName); | |
| 1638 | 14 | assert paramSymbol != nil<const SymbolTableEntry*>; | |
| 1639 | // Retrieve type of param | ||
| 1640 | 14 | const QualType paramSymbolType = paramSymbol.getQualType(); | |
| 1641 | // Add it to the lists | ||
| 1642 | 14 | paramInfoList.pushBack(ParamInfo{param.varName, paramSymbol}); | |
| 1643 | 14 | paramSymbolTypes.pushBack(paramSymbolType); | |
| 1644 | 14 | paramTypes.pushBack(paramSymbolType.getParamLLVMType(this.sourceFile)); | |
| 1645 | 14 | } | |
| 1646 | 7 | } | |
| 1647 | |||
| 1648 | // Build the function | ||
| 1649 | 748 | const llvm::Type returnType = this.builder.getInt32Ty(); | |
| 1650 | 748 | const llvm::Type fctType = llvm::getFunctionType(returnType, paramTypes, false); | |
| 1651 | 748 | llvm::Function fct = this.module.createFunction(fctType, llvm::LLVMLinkage::ExternalLinkage, MAIN_FUNCTION_NAME); | |
| 1652 | // ToDo: Set dso_local as soon as the LLVM C API allows it | ||
| 1653 | |||
| 1654 | // Add function attributes. The main function is the entry point, so nobody can call or inline it. | ||
| 1655 | 748 | fct.addFnAttr("mustprogress"); | |
| 1656 | 748 | fct.addFnAttr("noinline"); | |
| 1657 | 748 | fct.addFnAttr("norecurse"); | |
| 1658 | 748 | this.addCommonFctAttrs(fct); | |
| 1659 | 748 | this.enableFunctionInstrumentation(fct); | |
| 1660 | |||
| 1661 | // Add return value attributes | ||
| 1662 | 748 | fct.addRetAttr("noundef"); | |
| 1663 | |||
| 1664 | // Add debug info | ||
| 1665 | 748 | if this.cliOptions.instrumentation.emitsDebugInfo() { | |
| 1666 | 22 | const model::Function spiceFunc = createMainFunction(node.entry, paramSymbolTypes, cast<ASTNode*>(node)); | |
| 1667 | 22 | this.diGenerator.generateFunctionDebugInfo(fct, &spiceFunc); | |
| 1668 | 22 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 1669 | 22 | } | |
| 1670 | |||
| 1671 | // Create entry block | ||
| 1672 | 748 | const llvm::BasicBlock bEntry = this.createBlock(); | |
| 1673 | 748 | this.switchToBlock(bEntry, fct); | |
| 1674 | |||
| 1675 | // Reset alloca insert markers to this block | ||
| 1676 | 748 | this.allocaInsertBlock = bEntry; | |
| 1677 | 748 | this.allocaInsertInst = llvm::Value(); | |
| 1678 | |||
| 1679 | // Result variable | ||
| 1680 | 748 | const SymbolTableEntry* resultEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME)); | |
| 1681 | 748 | assert resultEntry != nil<const SymbolTableEntry*>; | |
| 1682 | 748 | if resultEntry.isInitialized() { | |
| 1683 | // Allocate result variable | ||
| 1684 | 8 | const llvm::Value resultAddr = this.insertAlloca(QualType(SuperType::TY_INT), String(RETURN_VARIABLE_NAME)); | |
| 1685 | // Update the symbol table entry | ||
| 1686 | 8 | this.updateAddress(resultEntry, resultAddr); | |
| 1687 | // Generate debug info | ||
| 1688 | 8 | this.diGenerator.generateLocalVarDebugInfo(String(RETURN_VARIABLE_NAME), resultAddr); | |
| 1689 | 8 | } | |
| 1690 | |||
| 1691 | // Store function argument values | ||
| 1692 | 748 | const unsigned int argCount = fct.argSize(); | |
| 1693 | 762 | for unsigned int argNumber = 0; argNumber < argCount; argNumber++ { | |
| 1694 | 14 | const llvm::Value arg = fct.getArg(argNumber); | |
| 1695 | // Get information about the parameter | ||
| 1696 | 14 | const ParamInfo& paramInfo = paramInfoList.get(argNumber); | |
| 1697 | 14 | const SymbolTableEntry* paramSymbol = paramInfo.entry; | |
| 1698 | 14 | assert paramSymbol != nil<const SymbolTableEntry*>; | |
| 1699 | // Decayed array params already carry the address of the array, so they do not need a local copy | ||
| 1700 | 14 | if this.bindDecayedArrayParam(arg, argNumber, paramInfo.name, paramSymbol) { | |
| 1701 | ✗ | continue; | |
| 1702 | } | ||
| 1703 | // Allocate space for it | ||
| 1704 | 14 | const llvm::Value paramAddress = this.insertAlloca(paramSymbol.getQualType(), paramInfo.name); | |
| 1705 | // Update the symbol table entry | ||
| 1706 | 14 | this.updateAddress(paramSymbol, paramAddress); | |
| 1707 | // Generate debug info | ||
| 1708 | 14 | this.diGenerator.generateLocalVarDebugInfo(paramInfo.name, paramAddress, cast<unsigned long>(argNumber) + 1l); | |
| 1709 | // Store the value at the new address | ||
| 1710 | 14 | this.insertStore(arg, paramAddress); | |
| 1711 | 14 | } | |
| 1712 | |||
| 1713 | // Visit function body | ||
| 1714 | 748 | this.visit(node.body); | |
| 1715 | |||
| 1716 | // Create return statement if the block is not terminated yet | ||
| 1717 | 748 | if !this.blockAlreadyTerminated { | |
| 1718 | 680 | llvm::Value res = this.getInt32(0l); | |
| 1719 | 680 | if resultEntry.isInitialized() { | |
| 1720 | 8 | res = this.insertLoad(fct.getReturnType(), this.getAddress(resultEntry)); | |
| 1721 | 8 | } | |
| 1722 | 680 | this.builder.createRet(res); | |
| 1723 | 680 | } | |
| 1724 | |||
| 1725 | // Conclude debug info for function | ||
| 1726 | 748 | this.diGenerator.concludeFunctionDebugInfo(); | |
| 1727 | |||
| 1728 | // Verify function | ||
| 1729 | 748 | this.verifyFunction(fct, node.codeLoc); | |
| 1730 | |||
| 1731 | // Change back to root scope | ||
| 1732 | 748 | this.currentScope = this.rootScope; | |
| 1733 | 748 | assert this.currentScope != nil<Scope*>; | |
| 1734 | |||
| 1735 | 1496 | return Any(); | |
| 1736 | 748 | } | |
| 1737 | |||
| 1738 | 26857 | public f<Any> IRGenerator.visitFctDef(ASTFctDefNode* node) { | |
| 1739 | 26857 | return this.visitFctOrProcDef(cast<ASTNode*>(node), &node.node, true, false); | |
| 1740 | } | ||
| 1741 | |||
| 1742 | 16732 | public f<Any> IRGenerator.visitProcDef(ASTProcDefNode* node) { | |
| 1743 | 16732 | return this.visitFctOrProcDef(cast<ASTNode*>(node), &node.node, false, node.isCtor); | |
| 1744 | } | ||
| 1745 | |||
| 1746 | /** | ||
| 1747 | * Shared implementation of visitFctDef and visitProcDef. The differences are marked below. | ||
| 1748 | * | ||
| 1749 | * @param baseNode Function or procedure definition node | ||
| 1750 | * @param node Function definition base of the node | ||
| 1751 | * @param isFunction Function or procedure | ||
| 1752 | * @param isCtor Whether the procedure is a constructor | ||
| 1753 | */ | ||
| 1754 | 43589 | f<Any> IRGenerator.visitFctOrProcDef(ASTNode* baseNode, ASTFctDefBaseNode* node, bool isFunction, bool isCtor) { | |
| 1755 | // Loop through manifestations | ||
| 1756 | 43589 | this.manIdx = 0l; // Reset the symbolTypeIndex | |
| 1757 | 101782 | foreach const model::Function* manifestation : node.manifestations { | |
| 1758 | 58193 | assert manifestation.entry != nil<SymbolTableEntry*>; | |
| 1759 | |||
| 1760 | // Check if the manifestation is substantiated or not public and not used by anybody | ||
| 1761 | 58193 | const QualType& entryType = manifestation.entry.getQualType(); | |
| 1762 | 58193 | const bool isPublic = isExportedFunction(manifestation, this.cliOptions.generateTestMain); | |
| 1763 | 58193 | if !manifestation.isFullySubstantiated() || (!isPublic && !manifestation.used) { | |
| 1764 | 15063 | this.manIdx++; // Increment symbolTypeIndex | |
| 1765 | 15063 | continue; | |
| 1766 | } | ||
| 1767 | 43130 | if !isFunction { | |
| 1768 | 16585 | assert manifestation.alreadyTypeChecked; | |
| 1769 | 16585 | } | |
| 1770 | |||
| 1771 | // Change to struct scope or to the interface scope for default methods | ||
| 1772 | 43130 | if manifestation.isMethod() { | |
| 1773 | 26177 | const QualType& thisType = manifestation.thisType; | |
| 1774 | 26177 | const QualTypeList noTemplateTypes; | |
| 1775 | 26177 | const String scopeName = manifestation.isInterfaceDefaultMethod ? getInterfaceScopeName(thisType.getSubType(), noTemplateTypes) : getStructScopeName(thisType.getSubType(), thisType.getTemplateTypes()); | |
| 1776 | 26177 | this.currentScope = this.currentScope.getChildScope(scopeName); | |
| 1777 | 26177 | assert this.currentScope != nil<Scope*>; | |
| 1778 | 26177 | } | |
| 1779 | |||
| 1780 | // Change scope | ||
| 1781 | 43130 | this.currentScope = this.currentScope.getChildScope(manifestation.getScopeName()); | |
| 1782 | 43130 | assert this.currentScope != nil<Scope*>; | |
| 1783 | |||
| 1784 | // Get 'this' entry | ||
| 1785 | 43130 | ParamInfoList paramInfoList; | |
| 1786 | 43130 | Vector<llvm::Type> paramTypes; | |
| 1787 | 43130 | if manifestation.isMethod() { | |
| 1788 | 26177 | const SymbolTableEntry* thisEntry = this.currentScope.lookupStrict(String(THIS_VARIABLE_NAME)); | |
| 1789 | 26177 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 1790 | 26177 | paramInfoList.pushBack(ParamInfo{String(THIS_VARIABLE_NAME), thisEntry}); | |
| 1791 | 26177 | paramTypes.pushBack(this.builder.getPtrTy()); | |
| 1792 | 26177 | } | |
| 1793 | |||
| 1794 | // Visit parameters | ||
| 1795 | 43130 | unsigned long argIdx = 0l; | |
| 1796 | 43130 | if node.hasParams { | |
| 1797 | 31014 | const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(node.paramLst); | |
| 1798 | 31014 | const unsigned long numOfParams = manifestation.paramList.getSize(); | |
| 1799 | 31014 | const QualTypeList manifestationParamTypes = manifestation.getParamTypes(); | |
| 1800 | 31014 | paramInfoList.reserve(numOfParams); | |
| 1801 | 31014 | paramTypes.reserve(numOfParams); | |
| 1802 | 79943 | while argIdx < numOfParams { | |
| 1803 | 48929 | const ASTDeclStmtNode* param = paramLst.params.get(argIdx); | |
| 1804 | // Get symbol table entry of param | ||
| 1805 | 48929 | const SymbolTableEntry* paramSymbol = this.currentScope.lookupStrict(param.varName); | |
| 1806 | 48929 | assert paramSymbol != nil<const SymbolTableEntry*>; | |
| 1807 | 48929 | const QualType& paramSymbolType = manifestationParamTypes.get(argIdx); | |
| 1808 | // Retrieve type of param | ||
| 1809 | 48929 | const llvm::Type paramType = paramSymbolType.getParamLLVMType(this.sourceFile); | |
| 1810 | // Add it to the lists | ||
| 1811 | 48929 | paramInfoList.pushBack(ParamInfo{param.varName, paramSymbol}); | |
| 1812 | 48929 | paramTypes.pushBack(paramType); | |
| 1813 | 48929 | argIdx++; | |
| 1814 | 48929 | } | |
| 1815 | 31014 | } | |
| 1816 | |||
| 1817 | // Get return type | ||
| 1818 | 43130 | llvm::Type returnType = this.builder.getVoidTy(); | |
| 1819 | 43130 | const QualType returnSType = isFunction ? manifestation.returnType : QualType(SuperType::TY_DYN); | |
| 1820 | 43130 | if isFunction { | |
| 1821 | 26545 | returnType = manifestation.returnType.toLLVMType(this.sourceFile); | |
| 1822 | 26545 | } | |
| 1823 | 43130 | const unsigned int argOffset = this.getArgOffset(returnSType); | |
| 1824 | |||
| 1825 | // Create function or implement declared function | ||
| 1826 | 43130 | const String mangledName = manifestation.getMangledName(); | |
| 1827 | 43130 | const llvm::Type fctType = this.getFunctionType(returnSType, paramTypes); | |
| 1828 | 43130 | this.module.getOrInsertFunction(mangledName.getRaw(), fctType); | |
| 1829 | 43130 | llvm::Function func = this.module.getFunction(mangledName.getRaw()); | |
| 1830 | 43130 | this.updateAddress(node.entry, func.asValue()); | |
| 1831 | 43130 | this.setLLVMFunction(manifestation, func); | |
| 1832 | 43130 | assert func.isEmpty(); | |
| 1833 | |||
| 1834 | // Set attributes to function | ||
| 1835 | // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library) | ||
| 1836 | 43130 | func.setLinkage(this.getSymbolLinkageType(isPublic)); | |
| 1837 | 43130 | this.addCommonFctAttrs(func, entryType.isInline(), node.attrs); | |
| 1838 | 43130 | this.enableFunctionInstrumentation(func); | |
| 1839 | // Set attributes to function parameters and return value | ||
| 1840 | 43130 | this.setParamAttrs(func, paramInfoList, argOffset); | |
| 1841 | 43130 | if isFunction { | |
| 1842 | 26545 | this.setFunctionReturnValAttrs(func, manifestation.returnType); | |
| 1843 | 26545 | } | |
| 1844 | |||
| 1845 | // Add debug info | ||
| 1846 | 43130 | this.diGenerator.generateFunctionDebugInfo(func, manifestation); | |
| 1847 | 43130 | this.diGenerator.setSourceLocation(baseNode); | |
| 1848 | |||
| 1849 | // Create entry block | ||
| 1850 | 43130 | const llvm::BasicBlock bEntry = this.createBlock(); | |
| 1851 | 43130 | this.switchToBlock(bEntry, func); | |
| 1852 | |||
| 1853 | // Reset alloca insert markers to this block | ||
| 1854 | 43130 | this.allocaInsertBlock = bEntry; | |
| 1855 | 43130 | this.allocaInsertInst = llvm::Value(); | |
| 1856 | |||
| 1857 | // Result variable | ||
| 1858 | 43130 | const SymbolTableEntry* resultEntry = nil<const SymbolTableEntry*>; | |
| 1859 | 43130 | if isFunction { | |
| 1860 | 26545 | resultEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME)); | |
| 1861 | 26545 | assert resultEntry != nil<const SymbolTableEntry*>; | |
| 1862 | 26545 | if resultEntry.isInitialized() { | |
| 1863 | // Allocate result variable. If the value is returned via memory, the result variable lives in the caller's memory | ||
| 1864 | 1108 | llvm::Value resultAddr = getSRetArg(func); | |
| 1865 | 1108 | if resultAddr.isNull() { | |
| 1866 | 650 | resultAddr = this.insertAlloca(manifestation.returnType, String(RETURN_VARIABLE_NAME)); | |
| 1867 | 650 | } | |
| 1868 | // Update the symbol table entry | ||
| 1869 | 1108 | this.updateAddress(resultEntry, resultAddr); | |
| 1870 | // Generate debug info | ||
| 1871 | 1108 | this.diGenerator.generateLocalVarDebugInfo(String(RETURN_VARIABLE_NAME), resultAddr); | |
| 1872 | 1108 | } | |
| 1873 | 26545 | } | |
| 1874 | |||
| 1875 | // Store function argument values. The sret parameter is skipped | ||
| 1876 | 43130 | const unsigned int argCount = func.argSize() - argOffset; | |
| 1877 | 118236 | for unsigned int argNumber = 0; argNumber < argCount; argNumber++ { | |
| 1878 | 75106 | const llvm::Value arg = func.getArg(argOffset + argNumber); | |
| 1879 | // Get information about the parameter | ||
| 1880 | 75106 | const ParamInfo& paramInfo = paramInfoList.get(argNumber); | |
| 1881 | 75106 | const SymbolTableEntry* paramSymbol = paramInfo.entry; | |
| 1882 | 75106 | assert paramSymbol != nil<const SymbolTableEntry*>; | |
| 1883 | // Decayed array params already carry the address of the array, so they do not need a local copy | ||
| 1884 | 75106 | if this.bindDecayedArrayParam(arg, argNumber, paramInfo.name, paramSymbol) { | |
| 1885 | 18 | continue; | |
| 1886 | } | ||
| 1887 | // Allocate space for it | ||
| 1888 | 75088 | const llvm::Value paramAddress = this.insertAlloca(paramSymbol.getQualType(), paramInfo.name); | |
| 1889 | // Update the symbol table entry | ||
| 1890 | 75088 | this.updateAddress(paramSymbol, paramAddress); | |
| 1891 | // Set source location | ||
| 1892 | 75088 | this.diGenerator.setSourceLocation(paramSymbol.declNode); | |
| 1893 | // Generate debug info to declare variable | ||
| 1894 | 75088 | this.diGenerator.generateLocalVarDebugInfo(paramInfo.name, paramAddress, cast<unsigned long>(argNumber) + 1l); | |
| 1895 | // Store the value at the new address | ||
| 1896 | 75088 | this.insertStore(arg, paramAddress); | |
| 1897 | 75088 | } | |
| 1898 | |||
| 1899 | // Store the default values for optional function args | ||
| 1900 | 43130 | if node.paramLst != nil<IVisitable*> { | |
| 1901 | 31014 | const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(node.paramLst); | |
| 1902 | 33479 | while argIdx < paramLst.params.getSize() { | |
| 1903 | 2465 | ASTDeclStmtNode* param = paramLst.params.get(argIdx); | |
| 1904 | 2465 | this.visit(cast<IVisitable*>(param)); | |
| 1905 | 2465 | argIdx++; | |
| 1906 | 2465 | } | |
| 1907 | 31014 | } | |
| 1908 | |||
| 1909 | // Generate special ctor preamble before generating the body to store VTable, default field values, etc. if required | ||
| 1910 | 43130 | if isCtor { | |
| 1911 | 5372 | this.isInCtorBody = true; | |
| 1912 | 5372 | this.generateCtorBodyPreamble(this.currentScope); | |
| 1913 | 5372 | } | |
| 1914 | |||
| 1915 | // Visit function body | ||
| 1916 | 43130 | this.visit(node.body); | |
| 1917 | |||
| 1918 | 43130 | if isCtor { | |
| 1919 | 5372 | this.isInCtorBody = false; | |
| 1920 | 5372 | } | |
| 1921 | |||
| 1922 | // Create return statement if the block is not terminated yet | ||
| 1923 | 43130 | if !this.blockAlreadyTerminated { | |
| 1924 | 17413 | if isFunction { | |
| 1925 | 1141 | llvm::Value res = this.getDefaultValueForSymbolType(manifestation.returnType); | |
| 1926 | 1141 | if resultEntry.isInitialized() { | |
| 1927 | 1107 | const llvm::Value resultAddr = this.getAddress(resultEntry); | |
| 1928 | 1107 | res = resultAddr.isSameAs(getSRetArg(func)) ? llvm::Value() : this.insertLoad(returnType, resultAddr); | |
| 1929 | 1107 | } | |
| 1930 | 1141 | this.insertReturn(res); | |
| 1931 | 1141 | } else { | |
| 1932 | 16272 | this.builder.createRetVoid(); | |
| 1933 | 16272 | } | |
| 1934 | 17413 | } | |
| 1935 | |||
| 1936 | // Conclude debug info for function | ||
| 1937 | 43130 | this.diGenerator.concludeFunctionDebugInfo(); | |
| 1938 | |||
| 1939 | // Verify function | ||
| 1940 | 43130 | this.verifyFunction(func, baseNode.codeLoc); | |
| 1941 | |||
| 1942 | // Change to root scope | ||
| 1943 | 43130 | this.currentScope = this.rootScope; | |
| 1944 | |||
| 1945 | 43130 | this.manIdx++; // Increment symbolTypeIndex | |
| 1946 | 43130 | } | |
| 1947 | 43589 | this.manIdx = 0l; // Reset the symbolTypeIndex | |
| 1948 | |||
| 1949 | // Ensure that we are at the root scope again | ||
| 1950 | 43589 | assert this.currentScope == this.rootScope; | |
| 1951 | |||
| 1952 | 43589 | return Any(); | |
| 1953 | } | ||
| 1954 | |||
| 1955 | /** | ||
| 1956 | * Bind a function argument that carries a decayed array to its parameter symbol. Since the argument already is the | ||
| 1957 | * address of the array, it can be used as the address of the parameter directly, without allocating a local copy. | ||
| 1958 | * | ||
| 1959 | * @param arg Argument of the LLVM function | ||
| 1960 | * @param argNumber Number of the argument | ||
| 1961 | * @param paramName Name of the parameter | ||
| 1962 | * @param paramSymbol Symbol table entry of the parameter | ||
| 1963 | * @return Whether the argument was bound (false if the parameter does not carry a decayed array) | ||
| 1964 | */ | ||
| 1965 | 75371 | f<bool> IRGenerator.bindDecayedArrayParam(llvm::Value arg, unsigned int argNumber, const String& paramName, const SymbolTableEntry* paramSymbol) { | |
| 1966 | 75371 | if paramSymbol == nil<const SymbolTableEntry*> { | |
| 1967 | 136 | return false; | |
| 1968 | } | ||
| 1969 | 75235 | const QualType& paramType = paramSymbol.getQualType(); | |
| 1970 | 75235 | if !paramType.isDecayedArray() { | |
| 1971 | 75215 | return false; | |
| 1972 | } | ||
| 1973 | |||
| 1974 | 20 | arg.setName(paramName.getRaw()); | |
| 1975 | 20 | this.updateAddress(paramSymbol, arg); | |
| 1976 | // Set source location and generate debug info to declare the variable | ||
| 1977 | 20 | this.diGenerator.setSourceLocation(paramSymbol.declNode); | |
| 1978 | 20 | this.diGenerator.generateLocalVarDebugInfo(paramName, arg, cast<unsigned long>(argNumber) + 1l); | |
| 1979 | 20 | return true; | |
| 1980 | } | ||
| 1981 | |||
| 1982 | /** | ||
| 1983 | * Materialize an argument that is passed to a decayed array parameter, so that the callee receives the address of a | ||
| 1984 | * writable array. | ||
| 1985 | * | ||
| 1986 | * Two cases need fixing up: if the argument was produced as the array itself instead of its address, it has to be put | ||
| 1987 | * into memory. And if it was produced as the address of a read-only constant - array literals are emitted as global | ||
| 1988 | * constants - it has to be copied into a mutable stack slot, because the callee may assign through the parameter. The | ||
| 1989 | * latter matches how C frontends materialize array compound literals. | ||
| 1990 | * | ||
| 1991 | * @param argValue Value that was produced for the argument | ||
| 1992 | * @param paramType Type of the parameter | ||
| 1993 | * @return Address of the array to hand to the callee | ||
| 1994 | */ | ||
| 1995 | 26 | public f<llvm::Value> IRGenerator.materializeDecayedArrayArg(llvm::Value argValue, const QualType& paramType) { | |
| 1996 | 26 | assert paramType.isDecayedArray(); | |
| 1997 | 26 | const llvm::Type paramArrayType = paramType.toLLVMType(this.sourceFile); | |
| 1998 | |||
| 1999 | // The array itself was produced (e.g. as the result of an implicit cast) -> put it into memory | ||
| 2000 | 26 | const llvm::Type argType = argValue.getType(); | |
| 2001 | 26 | if !argType.isPointerTy() { | |
| 2002 | ✗ | const llvm::Value argAddress = this.insertAlloca(paramArrayType, String("arg.decay")); | |
| 2003 | ✗ | this.insertStore(argValue, argAddress); | |
| 2004 | ✗ | return argAddress; | |
| 2005 | ✗ | } | |
| 2006 | |||
| 2007 | // Look through constant offsets, because the implicit array-to-array cast GEPs into the global constant | ||
| 2008 | 26 | const llvm::Value global = argValue.stripInBoundsConstantOffsets(); | |
| 2009 | 26 | if !global.isGlobalVariable() || !global.isConstant() { | |
| 2010 | 17 | return argValue; | |
| 2011 | } | ||
| 2012 | |||
| 2013 | // Copy the constant into a mutable stack slot. Function matching does not enforce that the argument array has the | ||
| 2014 | // same size as the parameter array, so copy the smaller of the two to never read past the end of the global. | ||
| 2015 | 9 | const llvm::Type globalType = global.getValueType(); | |
| 2016 | 9 | const bool globalIsSmaller = this.module.getTypeAllocSize(globalType) < this.module.getTypeAllocSize(paramArrayType); | |
| 2017 | 9 | const llvm::Value argAddress = this.insertAlloca(paramArrayType, String("arg.decay")); | |
| 2018 | 9 | this.generateShallowCopy(argValue, globalIsSmaller ? globalType : paramArrayType, argAddress, false); | |
| 2019 | 18 | return argAddress; | |
| 2020 | 9 | } | |
| 2021 | |||
| 2022 | 43130 | p IRGenerator.setParamAttrs(llvm::Function function, const ParamInfoList& paramInfo, unsigned int argOffset = 0) { | |
| 2023 | 43130 | assert cast<unsigned long>(function.argSize()) == cast<unsigned long>(argOffset) + paramInfo.getSize(); | |
| 2024 | 118236 | for unsigned int paramIdx = 0; paramIdx < cast<unsigned int>(paramInfo.getSize()); paramIdx++ { | |
| 2025 | 75106 | const unsigned int i = argOffset + paramIdx; | |
| 2026 | 75106 | const ParamInfo& info = paramInfo.get(paramIdx); | |
| 2027 | 75106 | const QualType& paramType = info.entry.getQualType(); | |
| 2028 | |||
| 2029 | // NoUndef attribute | ||
| 2030 | 75106 | function.addParamAttr(i, "noundef"); | |
| 2031 | |||
| 2032 | 75106 | if paramType.isPtr() { | |
| 2033 | 27774 | const QualType pointeeSType = paramType.getContained(); | |
| 2034 | 27774 | const llvm::Type pointeeType = pointeeSType.toLLVMType(this.sourceFile); | |
| 2035 | 27774 | assert !pointeeType.isNull(); | |
| 2036 | // NonNull and dereferenceable attributes. Only 'this' is guaranteed to point to a valid object, other pointers may be nil | ||
| 2037 | 27774 | if info.name == THIS_VARIABLE_NAME { | |
| 2038 | 26177 | function.addParamAttr(i, "nonnull"); | |
| 2039 | 26177 | function.addDereferenceableParamAttr(i, this.module.getTypeStoreSize(pointeeType)); | |
| 2040 | 26177 | } | |
| 2041 | // Alignment attribute | ||
| 2042 | 27774 | function.addParamAttr(i, "align", cast<unsigned long>(this.module.getABITypeAlign(pointeeType))); | |
| 2043 | 27774 | } | |
| 2044 | |||
| 2045 | // ZExt or SExt attribute | ||
| 2046 | 75106 | const string extAttrKind = this.getExtAttrKindForType(paramType); | |
| 2047 | 75106 | if extAttrKind != nil<string> { | |
| 2048 | 9013 | function.addParamAttr(i, extAttrKind); | |
| 2049 | 9013 | } | |
| 2050 | 75106 | } | |
| 2051 | 43130 | } | |
| 2052 | |||
| 2053 | 31563 | p IRGenerator.setFunctionReturnValAttrs(llvm::Function function, const QualType& returnType) { | |
| 2054 | 31563 | if returnType.is(SuperType::TY_DYN) { | |
| 2055 | 658 | return; | |
| 2056 | } | ||
| 2057 | |||
| 2058 | // Values, that are returned via memory, have attributes on the sret parameter. Coerced values might contain padding | ||
| 2059 | 30905 | const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType); | |
| 2060 | 30905 | if returnABI.isIndirect() { | |
| 2061 | 3735 | this.addSRetParamAttrs(function, returnABI.memoryType); | |
| 2062 | 3735 | return; | |
| 2063 | } | ||
| 2064 | 27170 | if returnABI.isCoerced() { | |
| 2065 | 1066 | return; | |
| 2066 | } | ||
| 2067 | |||
| 2068 | // NoUndef attribute | ||
| 2069 | 26104 | function.addRetAttr("noundef"); | |
| 2070 | // ZExt or SExt attribute | ||
| 2071 | 26104 | const string extAttrKind = this.getExtAttrKindForType(returnType); | |
| 2072 | 26104 | if extAttrKind != nil<string> { | |
| 2073 | 11358 | function.addRetAttr(extAttrKind); | |
| 2074 | 11358 | } | |
| 2075 | 26104 | } | |
| 2076 | |||
| 2077 | // Get the name of the ext attribute kind (zeroext/signext) for the given type, or nil if none is required (llvm::Attribute::None) | ||
| 2078 | 271211 | f<string> IRGenerator.getExtAttrKindForType(const QualType& qualType) { | |
| 2079 | 271211 | if qualType.is(SuperType::TY_DYN) { | |
| 2080 | ✗ | return nil<string>; | |
| 2081 | } | ||
| 2082 | |||
| 2083 | 271211 | const llvm::Type llvmType = qualType.toLLVMType(this.sourceFile); | |
| 2084 | 271211 | const unsigned int maxExtWidth = this.cliOptions.targetTriple.isPPC() ? 64 : 32; | |
| 2085 | 271211 | if llvmType.isIntegerTy() && llvmType.getIntegerBitWidth() < maxExtWidth { | |
| 2086 | 44479 | return qualType.isSigned() ? "signext" : "zeroext"; | |
| 2087 | } | ||
| 2088 | 226732 | return nil<string>; | |
| 2089 | } | ||
| 2090 | |||
| 2091 | 2983 | public f<Any> IRGenerator.visitStructDef(ASTStructDefNode* node) { | |
| 2092 | // Get all substantiated structs which result from this struct def | ||
| 2093 | 2983 | Vector<Struct*> manifestations = node.structManifestations; | |
| 2094 | |||
| 2095 | // Sort the manifestations to prevent generating the struct types in the wrong order (in case of dependencies between | ||
| 2096 | // structs). Insertion sort, the lists are small | ||
| 2097 | 5078 | for unsigned long i = 1l; i < manifestations.getSize(); i++ { | |
| 2098 | 2095 | Struct* current = manifestations.get(i); | |
| 2099 | 2095 | unsigned long j = i; | |
| 2100 | 2095 | while j > 0l { | |
| 2101 | 2095 | Struct* previous = manifestations.get(j - 1l); | |
| 2102 | 2095 | if previous.manifestationIndex <= current.manifestationIndex { | |
| 2103 | 2095 | break; | |
| 2104 | } | ||
| 2105 | ✗ | Struct*& slot = manifestations.get(j); | |
| 2106 | ✗ | slot = previous; | |
| 2107 | ✗ | j--; | |
| 2108 | ✗ | } | |
| 2109 | 2095 | Struct*& targetSlot = manifestations.get(j); | |
| 2110 | 2095 | targetSlot = current; | |
| 2111 | 2095 | } | |
| 2112 | |||
| 2113 | 8061 | foreach Struct* spiceStruct : manifestations { | |
| 2114 | // Skip structs, that are not fully substantiated | ||
| 2115 | 5078 | if !spiceStruct.structBase.isFullySubstantiated() { | |
| 2116 | 1079 | continue; | |
| 2117 | } | ||
| 2118 | |||
| 2119 | // Do not generate this struct if it is private and used by nobody | ||
| 2120 | 3999 | const QualType& structEntryType = spiceStruct.entry.getQualType(); | |
| 2121 | 3999 | if !spiceStruct.used && !structEntryType.isPublic() { | |
| 2122 | 3 | continue; | |
| 2123 | } | ||
| 2124 | |||
| 2125 | // Change scope to struct scope, specific to substantiation | ||
| 2126 | 3996 | this.currentScope = spiceStruct.scope; | |
| 2127 | 3996 | assert this.currentScope != nil<Scope*>; | |
| 2128 | |||
| 2129 | // Set LLVM type to the struct entry | ||
| 2130 | 3996 | const SymbolTableEntry* structEntry = spiceStruct.entry; | |
| 2131 | 3996 | assert structEntry != nil<const SymbolTableEntry*>; | |
| 2132 | |||
| 2133 | // Generate VTable if required | ||
| 2134 | 3996 | if node.emitVTable { | |
| 2135 | 872 | this.generateVTable(&spiceStruct.structBase); | |
| 2136 | 872 | this.deferredVTableInitializations.pushBack(&spiceStruct.structBase); | |
| 2137 | 872 | } | |
| 2138 | |||
| 2139 | // Generate default ctor if required | ||
| 2140 | 3996 | const QualType& thisType = structEntry.getQualType(); | |
| 2141 | 3996 | const ArgList noArgs; | |
| 2142 | 3996 | const model::Function* ctorFunc = lookupFunction(this.currentScope, String(CTOR_FUNCTION_NAME), thisType, noArgs, true); | |
| 2143 | 3996 | if ctorFunc != nil<const model::Function*> && ctorFunc.implicitDefault { | |
| 2144 | 161 | this.generateDefaultCtor(ctorFunc); | |
| 2145 | 161 | } | |
| 2146 | |||
| 2147 | // Generate default copy ctor if required | ||
| 2148 | 3996 | ArgList args; | |
| 2149 | 3996 | args.pushBack(Pair<QualType, bool>(thisType.toConstRef(cast<ASTNode*>(node)), false /* always non-temporary */)); | |
| 2150 | 3996 | const model::Function* copyCtorFunc = lookupFunction(this.currentScope, String(CTOR_FUNCTION_NAME), thisType, args, true); | |
| 2151 | 3996 | if copyCtorFunc != nil<const model::Function*> && copyCtorFunc.implicitDefault { | |
| 2152 | 700 | this.generateDefaultCopyCtor(copyCtorFunc); | |
| 2153 | 700 | } | |
| 2154 | |||
| 2155 | // Generate default move ctor if required. We can't use FunctionManager::lookup with a non-const ref arg | ||
| 2156 | // here, because the lookup permits const-param-to-non-const-arg matching ("constify") and may return the | ||
| 2157 | // copy ctor as a false positive. findMoveCtor scans the manifestations directly and only matches the | ||
| 2158 | // strict move ctor signature. | ||
| 2159 | 3996 | const model::Function* moveCtorFunc = findMoveCtor(this.currentScope); | |
| 2160 | 3996 | if moveCtorFunc != nil<const model::Function*> && moveCtorFunc.implicitDefault { | |
| 2161 | 21 | this.generateDefaultMoveCtor(moveCtorFunc); | |
| 2162 | 21 | } | |
| 2163 | |||
| 2164 | // Generate default dtor if required | ||
| 2165 | 3996 | const model::Function* dtorFunc = lookupFunction(this.currentScope, String(DTOR_FUNCTION_NAME), thisType, noArgs, true); | |
| 2166 | 3996 | if dtorFunc != nil<const model::Function*> && dtorFunc.implicitDefault { | |
| 2167 | 553 | this.generateDefaultDtor(dtorFunc); | |
| 2168 | 553 | } | |
| 2169 | |||
| 2170 | // Return to root scope | ||
| 2171 | 3996 | this.currentScope = this.rootScope; | |
| 2172 | 3996 | assert this.currentScope != nil<Scope*>; | |
| 2173 | 3996 | } | |
| 2174 | |||
| 2175 | 5966 | return Any(); | |
| 2176 | 2983 | } | |
| 2177 | |||
| 2178 | 291 | public f<Any> IRGenerator.visitInterfaceDef(ASTInterfaceDefNode* node) { | |
| 2179 | // Get all substantiated structs which result from this struct def | ||
| 2180 | 291 | Vector<Interface*> manifestations = node.interfaceManifestations; | |
| 2181 | |||
| 2182 | // Sort the manifestations to prevent generating the struct types in the wrong order (in case of dependencies between | ||
| 2183 | // structs). Insertion sort, the lists are small | ||
| 2184 | 927 | for unsigned long i = 1l; i < manifestations.getSize(); i++ { | |
| 2185 | 636 | Interface* current = manifestations.get(i); | |
| 2186 | 636 | unsigned long j = i; | |
| 2187 | 636 | while j > 0l { | |
| 2188 | 636 | Interface* previous = manifestations.get(j - 1l); | |
| 2189 | 636 | if previous.manifestationIndex <= current.manifestationIndex { | |
| 2190 | 636 | break; | |
| 2191 | } | ||
| 2192 | ✗ | Interface*& slot = manifestations.get(j); | |
| 2193 | ✗ | slot = previous; | |
| 2194 | ✗ | j--; | |
| 2195 | ✗ | } | |
| 2196 | 636 | Interface*& targetSlot = manifestations.get(j); | |
| 2197 | 636 | targetSlot = current; | |
| 2198 | 636 | } | |
| 2199 | |||
| 2200 | 1218 | foreach Interface* spiceInterface : manifestations { | |
| 2201 | // Skip interfaces, that are not fully substantiated | ||
| 2202 | 927 | if !spiceInterface.structBase.isFullySubstantiated() { | |
| 2203 | 244 | continue; | |
| 2204 | } | ||
| 2205 | |||
| 2206 | // Do not generate this interface if it is private and used by nobody | ||
| 2207 | 683 | const QualType& interfaceEntryType = spiceInterface.entry.getQualType(); | |
| 2208 | 683 | if !spiceInterface.used && !interfaceEntryType.isPublic() { | |
| 2209 | 1 | continue; | |
| 2210 | } | ||
| 2211 | |||
| 2212 | // Generate VTable information | ||
| 2213 | 682 | this.generateVTable(&spiceInterface.structBase); | |
| 2214 | 682 | this.deferredVTableInitializations.pushBack(&spiceInterface.structBase); | |
| 2215 | 682 | } | |
| 2216 | |||
| 2217 | 582 | return Any(); | |
| 2218 | 291 | } | |
| 2219 | |||
| 2220 | 15 | public f<Any> IRGenerator.visitUnionDef(ASTUnionDefNode* _) { | |
| 2221 | 15 | return Any(); // Noop (unions carry no per-definition IR work; their LLVM type is materialized lazily on first use) | |
| 2222 | } | ||
| 2223 | |||
| 2224 | 436 | public f<Any> IRGenerator.visitEnumDef(ASTEnumDefNode* _) { | |
| 2225 | 436 | return Any(); // Noop (enums are high-level semantic-only structures) | |
| 2226 | } | ||
| 2227 | |||
| 2228 | 2893 | public f<Any> IRGenerator.visitGenericTypeDef(ASTGenericTypeDefNode* _) { | |
| 2229 | 2893 | return Any(); // Noop (generic types are high-level semantic-only structures) | |
| 2230 | } | ||
| 2231 | |||
| 2232 | 153 | public f<Any> IRGenerator.visitAliasDef(ASTAliasDefNode* _) { | |
| 2233 | 153 | return Any(); // Noop (alias definitions are high-level semantic-only structures) | |
| 2234 | } | ||
| 2235 | |||
| 2236 | 4183 | public f<Any> IRGenerator.visitGlobalVarDef(ASTGlobalVarDefNode* node) { | |
| 2237 | // Retrieve some information about the variable | ||
| 2238 | 4183 | assert node.entry != nil<SymbolTableEntry*>; | |
| 2239 | 4183 | const QualType& entryType = node.entry.getQualType(); | |
| 2240 | 4183 | const bool isPublic = entryType.isPublic(); | |
| 2241 | 4183 | const bool isConst = entryType.isConst(); | |
| 2242 | |||
| 2243 | // Get correct type and linkage type. Use the type of the entry, because the data type might be dyn | ||
| 2244 | 4183 | const llvm::Type varType = entryType.toLLVMType(this.sourceFile); | |
| 2245 | |||
| 2246 | // Create global var | ||
| 2247 | 4183 | const llvm::Value varAddress = this.module.getOrInsertGlobal(node.varName.getRaw(), varType); | |
| 2248 | 4183 | llvm::Value var = this.module.getNamedGlobal(node.varName.getRaw()); | |
| 2249 | // Set some attributes, based on the given information | ||
| 2250 | 4183 | var.setConstant(isConst); | |
| 2251 | 4183 | var.setLinkage(this.getSymbolLinkageType(isPublic)); | |
| 2252 | // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library) | ||
| 2253 | |||
| 2254 | // Set initializer | ||
| 2255 | 4183 | if node.hasValue { // Set the constant value as variable initializer | |
| 2256 | 4177 | const llvm::Value constantValue = this.visitConst(cast<IVisitable*>(node.constant)); | |
| 2257 | 4177 | var.setInitializer(constantValue); | |
| 2258 | 4177 | } else { | |
| 2259 | // Set the default value as variable initializer. This is required in every build mode, because a global without | ||
| 2260 | // initializer would only be an external declaration. Zero-initialized globals live in .bss, so this is for free. | ||
| 2261 | 6 | const llvm::Value constantValue = this.getDefaultValueForSymbolType(entryType); | |
| 2262 | 6 | var.setInitializer(constantValue); | |
| 2263 | 6 | } | |
| 2264 | |||
| 2265 | 4183 | this.updateAddress(node.entry, varAddress); | |
| 2266 | |||
| 2267 | // Add debug info | ||
| 2268 | 4183 | this.diGenerator.generateGlobalVarDebugInfo(var, node.entry); | |
| 2269 | |||
| 2270 | 4183 | return Any(); | |
| 2271 | } | ||
| 2272 | |||
| 2273 | 5018 | public f<Any> IRGenerator.visitExtDecl(ASTExtDeclNode* node) { | |
| 2274 | 5018 | const model::Function* spiceFunc = node.extFunction; | |
| 2275 | 5018 | assert spiceFunc != nil<const model::Function*>; | |
| 2276 | 5018 | const unsigned int argOffset = this.getArgOffset(spiceFunc.returnType); | |
| 2277 | |||
| 2278 | // Get arg types | ||
| 2279 | 5018 | const QualTypeList paramTypes = spiceFunc.getParamTypes(); | |
| 2280 | 5018 | Vector<llvm::Type> argTypes; | |
| 2281 | 5018 | argTypes.reserve(paramTypes.getSize()); | |
| 2282 | 15703 | foreach const QualType& paramType : paramTypes { | |
| 2283 | 10685 | argTypes.pushBack(paramType.getParamLLVMType(this.sourceFile)); | |
| 2284 | 10685 | } | |
| 2285 | |||
| 2286 | // Declare function | ||
| 2287 | 5018 | bool isVarArg = false; | |
| 2288 | 5018 | if node.argTypeLst != nil<IVisitable*> { | |
| 2289 | 4947 | const ASTTypeLstWithEllipsisNode* argTypeLst = downcast<ASTTypeLstWithEllipsisNode>(node.argTypeLst); | |
| 2290 | 4947 | isVarArg = argTypeLst.hasEllipsis; | |
| 2291 | 4947 | } | |
| 2292 | 5018 | const llvm::Type functionType = this.getFunctionType(spiceFunc.returnType, argTypes, isVarArg); | |
| 2293 | 5018 | const String mangledName = spiceFunc.getMangledName(); | |
| 2294 | 5018 | this.module.getOrInsertFunction(mangledName.getRaw(), functionType); | |
| 2295 | 5018 | llvm::Function fct = this.module.getFunction(mangledName.getRaw()); | |
| 2296 | |||
| 2297 | // The external function is assumed not to unwind into Spice code | ||
| 2298 | 5018 | fct.addFnAttr("nounwind"); | |
| 2299 | |||
| 2300 | // Add noundef attribute to all parameters and, for sub-32-bit integers, the ABI-required sext/zext attribute | ||
| 2301 | 15703 | for unsigned int i = 0; i < cast<unsigned int>(paramTypes.getSize()); i++ { | |
| 2302 | 10685 | fct.addParamAttr(argOffset + i, "noundef"); | |
| 2303 | 10685 | const string extAttrKind = this.getExtAttrKindForType(paramTypes.get(i)); | |
| 2304 | 10685 | if extAttrKind != nil<string> { | |
| 2305 | 22 | fct.addParamAttr(argOffset + i, extAttrKind); | |
| 2306 | 22 | } | |
| 2307 | 10685 | } | |
| 2308 | |||
| 2309 | // Add the same noundef/sext/zext treatment to the return value | ||
| 2310 | 5018 | this.setFunctionReturnValAttrs(fct, spiceFunc.returnType); | |
| 2311 | |||
| 2312 | // If the function should be imported as dll, add the dll attribute | ||
| 2313 | 5018 | if node.attrs != nil<IVisitable*> { | |
| 2314 | 1 | const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(node.attrs); | |
| 2315 | 1 | const String dllAttrName = String(ATTR_CORE_LINKER_DLL); | |
| 2316 | 1 | if attrs.attrLst.hasAttr(dllAttrName) { | |
| 2317 | ✗ | const CompileTimeValue dllAttrValue = attrs.attrLst.getAttrValueByName(dllAttrName); | |
| 2318 | ✗ | if dllAttrValue.getBoolValue() { | |
| 2319 | ✗ | fct.setDLLStorageClass(llvm::LLVMDLLStorageClass::DLLImport); | |
| 2320 | ✗ | } | |
| 2321 | ✗ | } | |
| 2322 | 1 | } | |
| 2323 | |||
| 2324 | 10036 | return Any(); | |
| 2325 | 5018 | } | |
| 2326 | |||
| 2327 | // ===================================================== Control structures ====================================================== | ||
| 2328 | |||
| 2329 | 11993 | public f<Any> IRGenerator.visitUnsafeBlock(ASTUnsafeBlockNode* node) { | |
| 2330 | // Change scope | ||
| 2331 | 11993 | ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::UNSAFE_BODY, cast<ASTNode*>(node)); | |
| 2332 | |||
| 2333 | // Visit instructions in the block | ||
| 2334 | 11993 | this.visit(node.body); | |
| 2335 | |||
| 2336 | 23986 | return Any(); | |
| 2337 | 11993 | } | |
| 2338 | |||
| 2339 | 5024 | public f<Any> IRGenerator.visitForLoop(ASTForLoopNode* node) { | |
| 2340 | // Create blocks | ||
| 2341 | 5024 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 2342 | 5024 | const llvm::BasicBlock bHead = this.createBlock("for.head." + codeLine); | |
| 2343 | 5024 | const llvm::BasicBlock bBody = this.createBlock("for.body." + codeLine); | |
| 2344 | 5024 | const llvm::BasicBlock bTail = this.createBlock("for.tail." + codeLine); | |
| 2345 | 5024 | const llvm::BasicBlock bExit = this.createBlock("for.exit." + codeLine); | |
| 2346 | |||
| 2347 | // Change to head scope | ||
| 2348 | 5024 | ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::LOOP_HEAD, cast<ASTNode*>(node)); | |
| 2349 | 5024 | Scope* bodyScope = this.currentScope.getChildScope(node.getBodyScopeId()); | |
| 2350 | |||
| 2351 | // Save the break/continue targets, paired with the scope to clean up to when jumping there. The head scope is cleaned up in | ||
| 2352 | // the exit block, so break does not clean it up itself | ||
| 2353 | 5024 | this.breakTargets.pushBack(BreakContinueTarget{bodyScope, bExit}); | |
| 2354 | 5024 | this.continueTargets.pushBack(BreakContinueTarget{bodyScope, bTail}); | |
| 2355 | |||
| 2356 | // Init statement | ||
| 2357 | 5024 | this.visit(node.initDecl); | |
| 2358 | // Create jump from original to head block | ||
| 2359 | 5024 | this.insertJump(bHead); | |
| 2360 | |||
| 2361 | // Switch to head block | ||
| 2362 | 5024 | this.switchToBlock(bHead); | |
| 2363 | // Condition evaluation | ||
| 2364 | 5024 | const llvm::Value condValue = this.resolveValueInExprScope(node.condAssign); | |
| 2365 | // Create conditional jump from head to body or exit block | ||
| 2366 | 5024 | this.insertCondJump(condValue, bBody, bExit); | |
| 2367 | |||
| 2368 | // Switch to body block | ||
| 2369 | 5024 | this.switchToBlock(bBody); | |
| 2370 | // Visit body | ||
| 2371 | 5024 | ScopeHandle bodyScopeHandle = ScopeHandle(&this.compilerPass, bodyScope, ScopeType::FOR_BODY); | |
| 2372 | 5024 | this.visit(node.body); | |
| 2373 | 5024 | bodyScopeHandle.leaveScopeEarly(); | |
| 2374 | // Create jump from body to tail block | ||
| 2375 | 5024 | this.insertJump(bTail); | |
| 2376 | |||
| 2377 | // Switch to tail block | ||
| 2378 | 5024 | this.switchToBlock(bTail); | |
| 2379 | // Inc statement | ||
| 2380 | { | ||
| 2381 | 5024 | ExprScopeHandle exprScopeHandle = ExprScopeHandle(&this.compilerPass, node.incAssign); | |
| 2382 | 5024 | this.visit(node.incAssign); | |
| 2383 | 5024 | const Scope* exprScope = exprScopeHandle.getExprScope(); | |
| 2384 | 5024 | if exprScope != nil<Scope*> { | |
| 2385 | 5024 | this.generateTemporariesCleanup(exprScope, getASTNode(node.incAssign)); | |
| 2386 | 5024 | } | |
| 2387 | 5024 | } | |
| 2388 | // Create jump from tail to head | ||
| 2389 | 5024 | this.insertJump(bHead); | |
| 2390 | |||
| 2391 | // Switch to exit block | ||
| 2392 | 5024 | this.switchToBlock(bExit); | |
| 2393 | // Clean up the head scope, e.g. destruct the loop variable | ||
| 2394 | 5024 | this.generateLoopHeadCleanup(cast<ASTNode*>(node), this.currentScope); | |
| 2395 | |||
| 2396 | // Pop break/continue targets | ||
| 2397 | 5024 | this.breakTargets.popBack(); | |
| 2398 | 5024 | this.continueTargets.popBack(); | |
| 2399 | |||
| 2400 | 10048 | return Any(); | |
| 2401 | 5024 | } | |
| 2402 | |||
| 2403 | 438 | public f<Any> IRGenerator.visitForeachLoop(ASTForeachLoopNode* node) { | |
| 2404 | // Create blocks | ||
| 2405 | 438 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 2406 | 438 | const llvm::BasicBlock bHead = this.createBlock("foreach.head." + codeLine); | |
| 2407 | 438 | const llvm::BasicBlock bBody = this.createBlock("foreach.body." + codeLine); | |
| 2408 | 438 | const llvm::BasicBlock bTail = this.createBlock("foreach.tail." + codeLine); | |
| 2409 | 438 | const llvm::BasicBlock bExit = this.createBlock("foreach.exit." + codeLine); | |
| 2410 | |||
| 2411 | // Change to head scope | ||
| 2412 | 438 | ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::LOOP_HEAD, cast<ASTNode*>(node)); | |
| 2413 | 438 | Scope* bodyScope = this.currentScope.getChildScope(node.getBodyScopeId()); | |
| 2414 | |||
| 2415 | // Save the break/continue targets, paired with the scope to clean up to when jumping there. The head scope is cleaned up in | ||
| 2416 | // the exit block, so break does not clean it up itself | ||
| 2417 | 438 | this.breakTargets.pushBack(BreakContinueTarget{bodyScope, bExit}); | |
| 2418 | 438 | this.continueTargets.pushBack(BreakContinueTarget{bodyScope, bTail}); | |
| 2419 | |||
| 2420 | // Resolve iterator | ||
| 2421 | 438 | const ForeachLoopData& data = node.data.get(this.manIdx); | |
| 2422 | 438 | IVisitable* iteratorAssignNode = node.iteratorAssign; | |
| 2423 | 438 | llvm::Value iteratorPtr; | |
| 2424 | 438 | if data.getIteratorFct != nil<model::Function*> { // The iteratorAssignExpr is of type Iterable | |
| 2425 | 400 | const llvm::Value iterablePtr = this.resolveAddress(iteratorAssignNode); | |
| 2426 | |||
| 2427 | 400 | llvm::Value iterator; | |
| 2428 | 400 | llvm::Value iteratorAddr; | |
| 2429 | 400 | const QualTypeList getIteratorParamTypes = data.getIteratorFct.getParamTypes(); | |
| 2430 | 400 | bool isArrayIterable = false; | |
| 2431 | 400 | if !data.getIteratorFct.isMethod() { | |
| 2432 | 7 | const QualType& firstParamType = getIteratorParamTypes.front(); | |
| 2433 | 7 | isArrayIterable = firstParamType.isArray(); | |
| 2434 | 7 | } | |
| 2435 | 400 | if isArrayIterable { // Array as iterable | |
| 2436 | // Call iterate() function from std/iterator/array-iterator | ||
| 2437 | 7 | const llvm::Function iterateFct = this.stdFunctionManager.getIterateFct(data.getIteratorFct); | |
| 2438 | 7 | const QualType iterableType = this.getEvaluatedType(iteratorAssignNode); | |
| 2439 | 7 | const unsigned long arraySize = cast<unsigned long>(iterableType.getArraySize()); | |
| 2440 | 7 | assert arraySize > 0l; | |
| 2441 | 7 | Vector<llvm::Value> args; | |
| 2442 | 7 | args.pushBack(iterablePtr); | |
| 2443 | 7 | args.pushBack(this.builder.getInt64(arraySize)); | |
| 2444 | 7 | iterator = this.insertCall(iterateFct, args, data.getIteratorFct.returnType, iteratorAddr); | |
| 2445 | 7 | } else { // Struct as iterable | |
| 2446 | // Call .getIterator() on iterable | ||
| 2447 | 393 | const llvm::Function getIteratorFct = this.stdFunctionManager.getIteratorFct(data.getIteratorFct); | |
| 2448 | 393 | Vector<llvm::Value> args; | |
| 2449 | 393 | args.pushBack(iterablePtr); | |
| 2450 | 393 | iterator = this.insertCall(getIteratorFct, args, data.getIteratorFct.returnType, iteratorAddr); | |
| 2451 | 393 | } | |
| 2452 | |||
| 2453 | // Resolve address of iterator. If it is returned via memory or coerced, it already lives in memory | ||
| 2454 | 400 | LLVMExprResult callResult; | |
| 2455 | 400 | if !iteratorAddr.isNull() { | |
| 2456 | 400 | callResult.ptr = iteratorAddr; | |
| 2457 | 400 | } else { | |
| 2458 | ✗ | callResult.value = iterator; | |
| 2459 | ✗ | } | |
| 2460 | 400 | callResult.node = iteratorAssignNode; | |
| 2461 | 400 | iteratorPtr = this.resolveAddress(callResult); | |
| 2462 | |||
| 2463 | // If an anonymous symbol exists, set its address | ||
| 2464 | 400 | const SymbolTableEntry* returnSymbol = this.currentScope.symbolTable.lookupAnonymous(getASTNode(iteratorAssignNode)); | |
| 2465 | 400 | if returnSymbol != nil<const SymbolTableEntry*> { | |
| 2466 | 1 | this.updateAddress(returnSymbol, iteratorPtr); | |
| 2467 | 1 | } | |
| 2468 | 400 | } else { // The iteratorAssignExpr is of type Iterator | |
| 2469 | 38 | iteratorPtr = this.resolveAddress(iteratorAssignNode); | |
| 2470 | 38 | } | |
| 2471 | |||
| 2472 | // Check we have an idx | ||
| 2473 | 438 | const bool hasIdx = node.idxVarDecl != nil<IVisitable*>; | |
| 2474 | // Retrieve item ref type | ||
| 2475 | 438 | if hasIdx { | |
| 2476 | 30 | assert data.getIdxFct != nil<model::Function*>; | |
| 2477 | 30 | } else { | |
| 2478 | 408 | assert data.getFct != nil<model::Function*>; | |
| 2479 | 408 | } | |
| 2480 | 438 | const QualType itemRefSTy = hasIdx ? data.getIdxFct.returnType : data.getFct.returnType; | |
| 2481 | |||
| 2482 | // Visit idx variable declaration if required | ||
| 2483 | 438 | const SymbolTableEntry* idxEntry = nil<const SymbolTableEntry*>; | |
| 2484 | 438 | llvm::Value idxAddress; | |
| 2485 | 438 | if hasIdx { | |
| 2486 | 30 | this.visit(node.idxVarDecl); | |
| 2487 | // Get address of idx variable | ||
| 2488 | 30 | const ASTDeclStmtNode* idxDeclNode = downcast<ASTDeclStmtNode>(node.idxVarDecl); | |
| 2489 | 30 | idxEntry = idxDeclNode.entries.get(this.manIdx); | |
| 2490 | 30 | idxAddress = this.getAddress(idxEntry); | |
| 2491 | 30 | assert !idxAddress.isNull(); | |
| 2492 | 30 | } | |
| 2493 | |||
| 2494 | // Create jump from original to head block | ||
| 2495 | 438 | this.insertJump(bHead); | |
| 2496 | |||
| 2497 | // Switch to head block | ||
| 2498 | 438 | this.switchToBlock(bHead); | |
| 2499 | 438 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 2500 | // Call .isValid() on iterator | ||
| 2501 | 438 | assert data.isValidFct != nil<model::Function*>; | |
| 2502 | 438 | const llvm::Function isValidFct = this.stdFunctionManager.getIteratorIsValidFct(data.isValidFct); | |
| 2503 | 438 | Vector<llvm::Value> iteratorArgs; | |
| 2504 | 438 | iteratorArgs.pushBack(iteratorPtr); | |
| 2505 | 438 | const llvm::Value isValid = this.builder.createCall(isValidFct, iteratorArgs); | |
| 2506 | // Create conditional jump from head to body or exit block | ||
| 2507 | 438 | this.insertCondJump(isValid, bBody, bExit); | |
| 2508 | |||
| 2509 | // Switch to body block | ||
| 2510 | 438 | this.switchToBlock(bBody); | |
| 2511 | // Change to body scope | ||
| 2512 | 438 | ScopeHandle bodyScopeHandle = ScopeHandle(&this.compilerPass, bodyScope, ScopeType::FOREACH_BODY); | |
| 2513 | |||
| 2514 | // Visit item variable declaration. The item belongs to the body, so its lifetime begins anew in each round | ||
| 2515 | 438 | this.visit(node.itemVarDecl); | |
| 2516 | // Get address of item variable | ||
| 2517 | 438 | const ASTDeclStmtNode* itemDeclNode = downcast<ASTDeclStmtNode>(node.itemVarDecl); | |
| 2518 | 438 | const SymbolTableEntry* itemEntry = itemDeclNode.entries.get(this.manIdx); | |
| 2519 | 438 | const llvm::Value itemAddress = this.getAddress(itemEntry); | |
| 2520 | 438 | assert !itemAddress.isNull(); | |
| 2521 | |||
| 2522 | // Get the current iterator values | ||
| 2523 | 438 | LLVMExprResult itemResult; | |
| 2524 | 438 | if hasIdx { | |
| 2525 | 30 | const QualType& pairSTy = data.getIdxFct.returnType; | |
| 2526 | 30 | const llvm::Type pairTy = pairSTy.toLLVMType(this.sourceFile); | |
| 2527 | // Call .getIdx() on iterator. If the pair is returned via memory or coerced, it already lives in memory | ||
| 2528 | 30 | assert data.getIdxFct != nil<model::Function*>; | |
| 2529 | 30 | const llvm::Function getIdxFct = this.stdFunctionManager.getIteratorGetIdxFct(data.getIdxFct); | |
| 2530 | 30 | llvm::Value pairPtr; | |
| 2531 | 30 | llvm::Value pair = this.insertCall(getIdxFct, iteratorArgs, pairSTy, pairPtr); | |
| 2532 | 30 | if pairPtr.isNull() { | |
| 2533 | ✗ | pairPtr = this.insertAlloca(pairSTy, String("pair.addr")); | |
| 2534 | ✗ | pair.setName("pair"); | |
| 2535 | ✗ | this.insertStore(pair, pairPtr); | |
| 2536 | ✗ | } | |
| 2537 | // Store idx to idx var | ||
| 2538 | 30 | const llvm::Value idxAddrInPair = this.insertStructGEP(pairTy, pairPtr, 0, String("idx.addr")); | |
| 2539 | 30 | LLVMExprResult idxResult; | |
| 2540 | 30 | idxResult.ptr = idxAddrInPair; | |
| 2541 | 30 | assert !idxAddress.isNull() && idxEntry != nil<const SymbolTableEntry*>; | |
| 2542 | 30 | this.doAssignment(idxAddress, idxEntry, idxResult, QualType(SuperType::TY_LONG), cast<ASTNode*>(node), true); | |
| 2543 | // Store item to item var | ||
| 2544 | 30 | itemResult.refPtr = this.insertStructGEP(pairTy, pairPtr, 1, String("item.addr")); | |
| 2545 | 30 | } else { | |
| 2546 | // Call .get() on iterator | ||
| 2547 | 408 | assert data.getFct != nil<model::Function*>; | |
| 2548 | 408 | const llvm::Function getFct = this.stdFunctionManager.getIteratorGetFct(data.getFct); | |
| 2549 | 408 | itemResult.ptr = this.builder.createCall(getFct, iteratorArgs); | |
| 2550 | 408 | } | |
| 2551 | 438 | if data.calledItemCopyCtor != nil<model::Function*> { | |
| 2552 | // Call copy ctor | ||
| 2553 | 10 | const llvm::Value rhsAddress = this.resolveAddress(itemResult); | |
| 2554 | 10 | assert !rhsAddress.isNull(); | |
| 2555 | 10 | Vector<llvm::Value> ctorArgs; | |
| 2556 | 10 | ctorArgs.pushBack(rhsAddress); | |
| 2557 | 10 | this.generateCtorOrDtorCall(itemEntry, data.calledItemCopyCtor, ctorArgs); | |
| 2558 | 10 | } else { | |
| 2559 | // Perform normal assignment | ||
| 2560 | 428 | this.doAssignment(itemAddress, itemEntry, itemResult, itemRefSTy, cast<ASTNode*>(node), true); | |
| 2561 | 428 | } | |
| 2562 | // Visit body | ||
| 2563 | 438 | this.visit(node.body); | |
| 2564 | // Leave body scope | ||
| 2565 | 438 | bodyScopeHandle.leaveScopeEarly(); | |
| 2566 | // Create jump from body to tail block | ||
| 2567 | 438 | this.insertJump(bTail); | |
| 2568 | |||
| 2569 | // Switch to tail block | ||
| 2570 | 438 | this.switchToBlock(bTail); | |
| 2571 | 438 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 2572 | // Call .next() on iterator | ||
| 2573 | 438 | assert data.nextFct != nil<model::Function*>; | |
| 2574 | 438 | const llvm::Function nextFct = this.stdFunctionManager.getIteratorNextFct(data.nextFct); | |
| 2575 | 438 | this.builder.createCall(nextFct, iteratorArgs); | |
| 2576 | // Create jump from tail to head block | ||
| 2577 | 438 | this.insertJump(bHead); | |
| 2578 | |||
| 2579 | // Switch to exit block | ||
| 2580 | 438 | this.switchToBlock(bExit); | |
| 2581 | // Clean up the head scope, e.g. end the lifetime of the index variable | ||
| 2582 | 438 | this.generateLoopHeadCleanup(cast<ASTNode*>(node), this.currentScope); | |
| 2583 | |||
| 2584 | // Pop break/continue targets | ||
| 2585 | 438 | this.breakTargets.popBack(); | |
| 2586 | 438 | this.continueTargets.popBack(); | |
| 2587 | |||
| 2588 | 876 | return Any(); | |
| 2589 | 438 | } | |
| 2590 | |||
| 2591 | 1796 | public f<Any> IRGenerator.visitWhileLoop(ASTWhileLoopNode* node) { | |
| 2592 | // Create blocks | ||
| 2593 | 1796 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 2594 | 1796 | const llvm::BasicBlock bHead = this.createBlock("while.head." + codeLine); | |
| 2595 | 1796 | const llvm::BasicBlock bBody = this.createBlock("while.body." + codeLine); | |
| 2596 | 1796 | const llvm::BasicBlock bExit = this.createBlock("while.exit." + codeLine); | |
| 2597 | |||
| 2598 | // Change scope | ||
| 2599 | 1796 | ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::WHILE_BODY, cast<ASTNode*>(node)); | |
| 2600 | |||
| 2601 | // Save the break/continue targets, paired with the scope to clean up to when jumping there | ||
| 2602 | 1796 | this.breakTargets.pushBack(BreakContinueTarget{this.currentScope, bExit}); | |
| 2603 | 1796 | this.continueTargets.pushBack(BreakContinueTarget{this.currentScope, bHead}); | |
| 2604 | |||
| 2605 | // Jump to head block | ||
| 2606 | 1796 | this.insertJump(bHead); | |
| 2607 | |||
| 2608 | // Switch to head block | ||
| 2609 | 1796 | this.switchToBlock(bHead); | |
| 2610 | // Evaluate condition | ||
| 2611 | 1796 | const llvm::Value condValue = this.resolveValueInExprScope(node.condition); | |
| 2612 | // Jump to body or exit block, depending on the condition | ||
| 2613 | 1796 | this.insertCondJump(condValue, bBody, bExit); | |
| 2614 | |||
| 2615 | // Switch to body block | ||
| 2616 | 1796 | this.switchToBlock(bBody); | |
| 2617 | // Visit body | ||
| 2618 | 1796 | this.visit(node.body); | |
| 2619 | // Create jump to head block | ||
| 2620 | 1796 | this.insertJump(bHead); | |
| 2621 | |||
| 2622 | // Switch to exit block | ||
| 2623 | 1796 | this.switchToBlock(bExit); | |
| 2624 | |||
| 2625 | // Pop break/continue targets | ||
| 2626 | 1796 | this.breakTargets.popBack(); | |
| 2627 | 1796 | this.continueTargets.popBack(); | |
| 2628 | |||
| 2629 | 3592 | return Any(); | |
| 2630 | 1796 | } | |
| 2631 | |||
| 2632 | 81 | public f<Any> IRGenerator.visitDoWhileLoop(ASTDoWhileLoopNode* node) { | |
| 2633 | // Create blocks | ||
| 2634 | 81 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 2635 | 81 | const llvm::BasicBlock bBody = this.createBlock("dowhile.body." + codeLine); | |
| 2636 | 81 | const llvm::BasicBlock bFoot = this.createBlock("dowhile.foot." + codeLine); | |
| 2637 | 81 | const llvm::BasicBlock bExit = this.createBlock("dowhile.exit." + codeLine); | |
| 2638 | |||
| 2639 | // Change scope | ||
| 2640 | 81 | ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::WHILE_BODY, cast<ASTNode*>(node)); | |
| 2641 | |||
| 2642 | // Save the break/continue targets, paired with the scope to clean up to when jumping there | ||
| 2643 | 81 | this.breakTargets.pushBack(BreakContinueTarget{this.currentScope, bExit}); | |
| 2644 | 81 | this.continueTargets.pushBack(BreakContinueTarget{this.currentScope, bFoot}); | |
| 2645 | |||
| 2646 | // Jump to body block | ||
| 2647 | 81 | this.insertJump(bBody); | |
| 2648 | |||
| 2649 | // Switch to body block | ||
| 2650 | 81 | this.switchToBlock(bBody); | |
| 2651 | // Visit body | ||
| 2652 | 81 | this.visit(node.body); | |
| 2653 | // Create jump to foot block | ||
| 2654 | 81 | this.insertJump(bFoot); | |
| 2655 | |||
| 2656 | // Switch to head block | ||
| 2657 | 81 | this.switchToBlock(bFoot); | |
| 2658 | // Evaluate condition | ||
| 2659 | 81 | const llvm::Value condValue = this.resolveValueInExprScope(node.condition); | |
| 2660 | // Jump to body or exit block, depending on the condition | ||
| 2661 | 81 | this.insertCondJump(condValue, bBody, bExit); | |
| 2662 | |||
| 2663 | // Switch to exit block | ||
| 2664 | 81 | this.switchToBlock(bExit); | |
| 2665 | |||
| 2666 | // Pop break/continue targets | ||
| 2667 | 81 | this.breakTargets.popBack(); | |
| 2668 | 81 | this.continueTargets.popBack(); | |
| 2669 | |||
| 2670 | 162 | return Any(); | |
| 2671 | 81 | } | |
| 2672 | |||
| 2673 | 26515 | public f<Any> IRGenerator.visitIfStmt(ASTIfStmtNode* node) { | |
| 2674 | // If we have a compile time decision, only evaluate the respective branch | ||
| 2675 | 26515 | const bool doCompileThenBranch = node.doCompileThenBranch(this.manIdx); | |
| 2676 | 26515 | const bool doCompileElseBranch = node.doCompileElseBranch(this.manIdx); | |
| 2677 | 26515 | if doCompileThenBranch && !doCompileElseBranch { | |
| 2678 | 236 | ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::IF_ELSE_BODY, cast<ASTNode*>(node)); | |
| 2679 | 236 | this.visit(node.thenBody); | |
| 2680 | 472 | return Any(this.builder.getTrue()); | |
| 2681 | 236 | } | |
| 2682 | 26279 | if !doCompileThenBranch && doCompileElseBranch { | |
| 2683 | 145 | if node.elseStmt != nil<IVisitable*> { | |
| 2684 | 2 | this.visit(node.elseStmt); | |
| 2685 | 2 | } | |
| 2686 | 145 | return Any(this.builder.getFalse()); | |
| 2687 | } | ||
| 2688 | |||
| 2689 | // Create blocks | ||
| 2690 | 26134 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 2691 | 26134 | const llvm::BasicBlock bThen = this.createBlock("if.then." + codeLine); | |
| 2692 | 26134 | llvm::BasicBlock bElse; | |
| 2693 | 26134 | if node.elseStmt != nil<IVisitable*> { | |
| 2694 | 1302 | bElse = this.createBlock("if.else." + codeLine); | |
| 2695 | 1302 | } | |
| 2696 | 26134 | const llvm::BasicBlock bExit = this.createBlock("if.exit." + codeLine); | |
| 2697 | |||
| 2698 | // Change scope | ||
| 2699 | 26134 | ScopeHandle scopeHandle = ScopeHandle(this, node.getScopeId(), ScopeType::IF_ELSE_BODY, cast<ASTNode*>(node)); | |
| 2700 | |||
| 2701 | // Retrieve condition value | ||
| 2702 | 26134 | const llvm::Value condValue = this.resolveValueInExprScope(node.condition); | |
| 2703 | // Check if condition is fulfilled | ||
| 2704 | 26134 | this.insertCondJump(condValue, bThen, node.elseStmt != nil<IVisitable*> ? bElse : bExit); | |
| 2705 | |||
| 2706 | // Switch to then block | ||
| 2707 | 26134 | this.switchToBlock(bThen); | |
| 2708 | // Visit then body | ||
| 2709 | 26134 | this.visit(node.thenBody); | |
| 2710 | // Create jump from then to end block | ||
| 2711 | 26134 | this.insertJump(bExit); | |
| 2712 | |||
| 2713 | // Change scope back | ||
| 2714 | 26134 | scopeHandle.leaveScopeEarly(); | |
| 2715 | |||
| 2716 | 26134 | if node.elseStmt != nil<IVisitable*> { | |
| 2717 | // Switch to else block | ||
| 2718 | 1302 | this.switchToBlock(bElse); | |
| 2719 | // Visit else block | ||
| 2720 | 1302 | this.visit(node.elseStmt); | |
| 2721 | // Create jump from else to end block | ||
| 2722 | 1302 | this.insertJump(bExit); | |
| 2723 | 1302 | } | |
| 2724 | |||
| 2725 | // Switch to exit block | ||
| 2726 | 26134 | this.switchToBlock(bExit); | |
| 2727 | |||
| 2728 | // Return conditional value as result for the 'if' stmt | ||
| 2729 | 52268 | return Any(condValue); | |
| 2730 | 26134 | } | |
| 2731 | |||
| 2732 | 1304 | public f<Any> IRGenerator.visitElseStmt(ASTElseStmtNode* node) { | |
| 2733 | 1304 | if node.ifStmt != nil<IVisitable*> { // It is an else if branch | |
| 2734 | 550 | this.visit(node.ifStmt); | |
| 2735 | 550 | } else { // It is an else branch | |
| 2736 | // Change scope | ||
| 2737 | 754 | ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::IF_ELSE_BODY, cast<ASTNode*>(node)); | |
| 2738 | |||
| 2739 | // Generate IR for nested statements | ||
| 2740 | 754 | this.visit(node.body); | |
| 2741 | 754 | } | |
| 2742 | |||
| 2743 | 1304 | return Any(); | |
| 2744 | } | ||
| 2745 | |||
| 2746 | 30 | public f<Any> IRGenerator.visitSwitchStmt(ASTSwitchStmtNode* node) { | |
| 2747 | // Create blocks | ||
| 2748 | 30 | Vector<llvm::BasicBlock> bCases; | |
| 2749 | 30 | bCases.reserve(node.caseBranches.getSize()); | |
| 2750 | 213 | foreach IVisitable* caseBranch : node.caseBranches { | |
| 2751 | 183 | const CodeLoc& caseBranchCodeLoc = caseBranch.getCodeLoc(); | |
| 2752 | 183 | bCases.pushBack(this.createBlock("switch.case." + caseBranchCodeLoc.toPrettyLine())); | |
| 2753 | 183 | } | |
| 2754 | 30 | llvm::BasicBlock bDefault; | |
| 2755 | 30 | if node.hasDefaultBranch { | |
| 2756 | 18 | const CodeLoc& defaultBranchCodeLoc = node.defaultBranch.getCodeLoc(); | |
| 2757 | 18 | bDefault = this.createBlock("switch.default." + defaultBranchCodeLoc.toPrettyLine()); | |
| 2758 | 18 | } | |
| 2759 | 30 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 2760 | 30 | const llvm::BasicBlock bExit = this.createBlock("switch.exit." + codeLine); | |
| 2761 | |||
| 2762 | // Save the break target. The scope to clean up to is set by each case/default branch as it is visited below, | ||
| 2763 | // since it changes per branch (there is no single scope spanning the whole switch statement). | ||
| 2764 | 30 | this.breakTargets.pushBack(BreakContinueTarget{nil<Scope*>, bExit}); | |
| 2765 | |||
| 2766 | // Visit switch expression | ||
| 2767 | 30 | const llvm::Value exprValue = this.resolveValue(node.assignExpr); | |
| 2768 | |||
| 2769 | // Generate switch instruction | ||
| 2770 | 30 | llvm::Value switchInst = this.builder.createSwitch(exprValue, node.hasDefaultBranch ? bDefault : bExit, cast<unsigned int>(node.caseBranches.getSize())); | |
| 2771 | |||
| 2772 | // Generate case branches | ||
| 2773 | 213 | for unsigned long i = 0l; i < node.caseBranches.getSize(); i++ { | |
| 2774 | 183 | const ASTCaseBranchNode* caseBranch = downcast<ASTCaseBranchNode>(node.caseBranches.get(i)); | |
| 2775 | |||
| 2776 | // Push fallthrough block | ||
| 2777 | 183 | llvm::BasicBlock bFallthrough = bDefault; | |
| 2778 | 183 | if i + 1l < node.caseBranches.getSize() { | |
| 2779 | 153 | bFallthrough = bCases.get(i + 1l); | |
| 2780 | 153 | } | |
| 2781 | 183 | this.fallthroughBlocks.push(bFallthrough); | |
| 2782 | |||
| 2783 | // Switch to case block | ||
| 2784 | 183 | this.switchToBlock(bCases.get(i)); | |
| 2785 | |||
| 2786 | // Visit case body | ||
| 2787 | 183 | this.visit(node.caseBranches.get(i)); | |
| 2788 | |||
| 2789 | // Create jump from case to exit block | ||
| 2790 | 183 | this.insertJump(bExit); | |
| 2791 | |||
| 2792 | // Pop fallthrough block | ||
| 2793 | 183 | this.fallthroughBlocks.pop(); | |
| 2794 | |||
| 2795 | // Add case to switch instruction | ||
| 2796 | 371 | foreach IVisitable* caseConstantNode : caseBranch.caseConstants { | |
| 2797 | 188 | const llvm::Value caseValue = this.visitConst(caseConstantNode); | |
| 2798 | 188 | switchInst.addCase(caseValue, bCases.get(i)); | |
| 2799 | 188 | } | |
| 2800 | 183 | } | |
| 2801 | |||
| 2802 | // Generate default branch | ||
| 2803 | 30 | if node.hasDefaultBranch { | |
| 2804 | // Switch to default block | ||
| 2805 | 18 | this.switchToBlock(bDefault); | |
| 2806 | |||
| 2807 | // Visit default body | ||
| 2808 | 18 | this.visit(node.defaultBranch); | |
| 2809 | |||
| 2810 | // Create jump from default to exit block | ||
| 2811 | 18 | this.insertJump(bExit); | |
| 2812 | 18 | } | |
| 2813 | |||
| 2814 | // Switch to exit block | ||
| 2815 | 30 | this.switchToBlock(bExit); | |
| 2816 | |||
| 2817 | // Pop break target | ||
| 2818 | 30 | this.breakTargets.popBack(); | |
| 2819 | |||
| 2820 | 60 | return Any(); | |
| 2821 | 30 | } | |
| 2822 | |||
| 2823 | 183 | public f<Any> IRGenerator.visitCaseBranch(ASTCaseBranchNode* node) { | |
| 2824 | // Change to case body scope | ||
| 2825 | 183 | ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::CASE_BODY); | |
| 2826 | |||
| 2827 | // A 'break' within this branch targets the enclosing switch; point it at this branch's own scope | ||
| 2828 | 183 | BreakContinueTarget& breakTarget = this.breakTargets.back(); | |
| 2829 | 183 | breakTarget.scope = this.currentScope; | |
| 2830 | |||
| 2831 | // Visit case body | ||
| 2832 | 183 | this.visit(node.body); | |
| 2833 | |||
| 2834 | 366 | return Any(); | |
| 2835 | 183 | } | |
| 2836 | |||
| 2837 | 18 | public f<Any> IRGenerator.visitDefaultBranch(ASTDefaultBranchNode* node) { | |
| 2838 | // Change to default body scope | ||
| 2839 | 18 | ScopeHandle _ = ScopeHandle(&this.compilerPass, node.getScopeId(), ScopeType::DEFAULT_BODY); | |
| 2840 | |||
| 2841 | // A 'break' within this branch targets the enclosing switch; point it at this branch's own scope | ||
| 2842 | 18 | BreakContinueTarget& breakTarget = this.breakTargets.back(); | |
| 2843 | 18 | breakTarget.scope = this.currentScope; | |
| 2844 | |||
| 2845 | // Visit case body | ||
| 2846 | 18 | this.visit(node.body); | |
| 2847 | |||
| 2848 | 36 | return Any(); | |
| 2849 | 18 | } | |
| 2850 | |||
| 2851 | 126 | public f<Any> IRGenerator.visitAnonymousBlockStmt(ASTAnonymousBlockStmtNode* node) { | |
| 2852 | // Change scope | ||
| 2853 | 126 | node.bodyScope.parent = this.currentScope; // Needed for nested scopes in generic functions | |
| 2854 | 126 | node.bodyScope.symbolTable.parent = &this.currentScope.symbolTable; // Needed for nested scopes in generic functions | |
| 2855 | 126 | ScopeHandle _ = ScopeHandle(this, node.getScopeId(), ScopeType::ANONYMOUS_BLOCK_BODY, cast<ASTNode*>(node)); | |
| 2856 | |||
| 2857 | // Visit instructions in the block | ||
| 2858 | 126 | this.visit(node.body); | |
| 2859 | |||
| 2860 | 252 | return Any(); | |
| 2861 | 126 | } | |
| 2862 | |||
| 2863 | // ========================================================= Statements ========================================================== | ||
| 2864 | |||
| 2865 | 90793 | public f<Any> IRGenerator.visitStmtLst(ASTStmtLstNode* node) { | |
| 2866 | // Generate instructions in the scope | ||
| 2867 | 271397 | foreach IVisitable* stmt : node.statements { | |
| 2868 | // Check if we can cancel generating instructions for this code branch | ||
| 2869 | 180612 | const ASTStmtNode* stmtNode = asStmtNode(getASTNode(stmt)); | |
| 2870 | 180612 | if this.blockAlreadyTerminated || stmtNode.unreachable { | |
| 2871 | 8 | break; | |
| 2872 | } | ||
| 2873 | |||
| 2874 | // Set source location for debug info | ||
| 2875 | 180604 | this.diGenerator.setSourceLocation(getASTNode(stmt)); | |
| 2876 | |||
| 2877 | // Visit child | ||
| 2878 | 180604 | this.visit(stmt); | |
| 2879 | 180604 | } | |
| 2880 | |||
| 2881 | // Generate cleanup code of this scope, e.g. dtor calls for struct instances | ||
| 2882 | 90793 | this.generateScopeCleanup(node); | |
| 2883 | |||
| 2884 | 90793 | return Any(); | |
| 2885 | } | ||
| 2886 | |||
| 2887 | ✗ | public f<Any> IRGenerator.visitTypeAltsLst(ASTTypeAltsLstNode* _) { | |
| 2888 | ✗ | return Any(); // Noop | |
| 2889 | } | ||
| 2890 | |||
| 2891 | 35014 | public f<Any> IRGenerator.visitDeclStmt(ASTDeclStmtNode* node) { | |
| 2892 | // Get variable entry | ||
| 2893 | 35014 | const SymbolTableEntry* varEntry = node.entries.get(this.manIdx); | |
| 2894 | 35014 | const DeclStmtData& data = node.data.get(this.manIdx); | |
| 2895 | 35014 | assert varEntry != nil<const SymbolTableEntry*>; | |
| 2896 | 35014 | const QualType varSymbolType = varEntry.getQualType(); | |
| 2897 | |||
| 2898 | // Get LLVM type of variable | ||
| 2899 | 35014 | const llvm::Type varTy = varSymbolType.toLLVMType(this.sourceFile); | |
| 2900 | |||
| 2901 | // Check if the declaration is with an assignment or the default value | ||
| 2902 | 35014 | llvm::Value varAddress; | |
| 2903 | 35014 | if node.hasAssignment { // Assignment | |
| 2904 | 33160 | if data.calledCopyCtor != nil<model::Function*> { | |
| 2905 | // Allocate memory | ||
| 2906 | 113 | varAddress = this.insertAlloca(varTy); | |
| 2907 | 113 | this.updateAddress(varEntry, varAddress); | |
| 2908 | // Generate debug info for variable declaration | ||
| 2909 | 113 | this.diGenerator.generateLocalVarDebugInfo(node.varName, varAddress); | |
| 2910 | // Call copy ctor | ||
| 2911 | 113 | const llvm::Value rhsAddress = this.resolveAddress(node.assignExpr); | |
| 2912 | 113 | assert !rhsAddress.isNull(); | |
| 2913 | 113 | Vector<llvm::Value> ctorArgs; | |
| 2914 | 113 | ctorArgs.pushBack(rhsAddress); | |
| 2915 | 113 | this.generateCtorOrDtorCall(varEntry, data.calledCopyCtor, ctorArgs); | |
| 2916 | 113 | } else { | |
| 2917 | // Assign rhs to lhs | ||
| 2918 | 33047 | const LLVMExprResult assignResult = this.doAssignment(varAddress, varEntry, node.assignExpr, cast<ASTNode*>(node), true); | |
| 2919 | 33047 | assert assignResult.entry == varEntry; | |
| 2920 | 33047 | varAddress = this.getAddress(varEntry); | |
| 2921 | 33047 | this.updateAddress(varEntry, varAddress); | |
| 2922 | 33047 | } | |
| 2923 | 33160 | } else { // Default value | |
| 2924 | // Allocate memory | ||
| 2925 | 1854 | varAddress = this.insertAlloca(varTy); | |
| 2926 | 1854 | this.updateAddress(varEntry, varAddress); | |
| 2927 | |||
| 2928 | // Generate debug info for variable declaration | ||
| 2929 | 1854 | this.diGenerator.generateLocalVarDebugInfo(node.varName, varAddress); | |
| 2930 | |||
| 2931 | 1854 | if data.calledInitCtor != nil<model::Function*> { | |
| 2932 | // Call no-args constructor | ||
| 2933 | 1004 | const Vector<llvm::Value> noArgs; | |
| 2934 | 1004 | this.generateCtorOrDtorCall(varEntry, data.calledInitCtor, noArgs); | |
| 2935 | // A union with no default field must start in the "unset" tag state, and a union with a default field must | ||
| 2936 | // start with that field active. Either way, the tag bits carry real runtime safety meaning (unlike a struct's | ||
| 2937 | // all-zero debug-only default), so they must be initialized unconditionally, in every build mode. | ||
| 2938 | 1854 | } else if !node.isForEachItem && (this.cliOptions.buildMode != BuildMode::RELEASE || varSymbolType.is(SuperType::TY_UNION)) { | |
| 2939 | 408 | assert !data.isCtorCallRequired; | |
| 2940 | // Retrieve default value for lhs symbol type and store it | ||
| 2941 | 408 | const llvm::Value defaultValue = this.getDefaultValueForSymbolType(varSymbolType); | |
| 2942 | 408 | this.insertStore(defaultValue, varAddress); | |
| 2943 | 850 | } | |
| 2944 | 1854 | } | |
| 2945 | 35014 | assert !varAddress.isNull(); | |
| 2946 | |||
| 2947 | // Attach the variable name to the LLVM value. | ||
| 2948 | 35014 | varAddress.setName(varEntry.name.getRaw()); | |
| 2949 | |||
| 2950 | 35014 | return Any(); | |
| 2951 | } | ||
| 2952 | |||
| 2953 | ✗ | public f<Any> IRGenerator.visitQualifierLst(ASTQualifierLstNode* _) { | |
| 2954 | ✗ | return Any(); // Noop | |
| 2955 | } | ||
| 2956 | |||
| 2957 | 1431 | public f<Any> IRGenerator.visitModAttr(ASTModAttrNode* _) { | |
| 2958 | 1431 | return Any(); // Noop | |
| 2959 | } | ||
| 2960 | |||
| 2961 | ✗ | public f<Any> IRGenerator.visitTopLevelDefinitionAttr(ASTTopLevelDefAttrNode* _) { | |
| 2962 | ✗ | return Any(); // Noop | |
| 2963 | } | ||
| 2964 | |||
| 2965 | 188 | public f<Any> IRGenerator.visitCaseConstant(ASTCaseConstantNode* node) { | |
| 2966 | 188 | if node.constant != nil<ASTConstantNode*> { | |
| 2967 | 93 | return this.visit(cast<IVisitable*>(node.constant)); | |
| 2968 | } | ||
| 2969 | |||
| 2970 | 95 | const SymbolTableEntry* constantEntry = node.entry; | |
| 2971 | 95 | const CompileTimeValue compileTimeValue = constantEntry.declNode.getCompileTimeValue(this.manIdx); | |
| 2972 | 95 | const QualType evaluatedType = this.getEvaluatedType(cast<IVisitable*>(node)); | |
| 2973 | 95 | return Any(this.getConst(compileTimeValue, evaluatedType, cast<ASTNode*>(node))); | |
| 2974 | } | ||
| 2975 | |||
| 2976 | 40855 | public f<Any> IRGenerator.visitReturnStmt(ASTReturnStmtNode* node) { | |
| 2977 | // If the value is returned via memory, it is directly put into the memory of the caller | ||
| 2978 | 40855 | const llvm::BasicBlock currentBlock = this.builder.getInsertBlock(); | |
| 2979 | 40855 | const llvm::Value sretArg = getSRetArg(currentBlock.getParent()); | |
| 2980 | |||
| 2981 | 40855 | llvm::Value returnValue; | |
| 2982 | 40855 | if node.hasReturnValue { // Return value is attached to the return statement | |
| 2983 | 36773 | IVisitable* returnExpr = node.assignExpr; | |
| 2984 | 36773 | const ReturnStmtData& data = node.data.get(this.manIdx); | |
| 2985 | 36773 | if data.calledCopyCtor != nil<model::Function*> { | |
| 2986 | // Perform a copy | ||
| 2987 | 364 | const llvm::Value originalAddress = this.resolveAddress(returnExpr); | |
| 2988 | 364 | const llvm::Type returnTy = data.returnType.toLLVMType(this.sourceFile); | |
| 2989 | 364 | const llvm::Value newAddress = sretArg.isNull() ? this.insertAlloca(returnTy) : sretArg; | |
| 2990 | 364 | Vector<llvm::Value> ctorArgs; | |
| 2991 | 364 | ctorArgs.pushBack(originalAddress); | |
| 2992 | 364 | this.generateCtorOrDtorCall(newAddress, data.calledCopyCtor, ctorArgs); | |
| 2993 | 364 | if sretArg.isNull() { | |
| 2994 | ✗ | returnValue = this.insertLoad(returnTy, newAddress); | |
| 2995 | ✗ | } | |
| 2996 | 364 | } else { | |
| 2997 | 36409 | returnValue = data.returnType.isRef() ? this.resolveAddress(returnExpr) : this.resolveValue(returnExpr); | |
| 2998 | 36409 | } | |
| 2999 | 36773 | } else { // Try to load result variable value | |
| 3000 | 4082 | const SymbolTableEntry* resultEntry = this.currentScope.lookupResultVar(); | |
| 3001 | 4082 | if resultEntry != nil<const SymbolTableEntry*> { | |
| 3002 | 5 | const llvm::Value returnValueAddr = this.getAddress(resultEntry); | |
| 3003 | // The result variable of functions, that return via memory, already lives in the memory of the caller | ||
| 3004 | 5 | if !returnValueAddr.isSameAs(sretArg) { | |
| 3005 | 3 | const QualType& resultType = resultEntry.getQualType(); | |
| 3006 | 3 | const llvm::Type resultSTy = resultType.toLLVMType(this.sourceFile); | |
| 3007 | 3 | returnValue = this.insertLoad(resultSTy, returnValueAddr); | |
| 3008 | 3 | } | |
| 3009 | 5 | } | |
| 3010 | 4082 | } | |
| 3011 | |||
| 3012 | // Clean up all scopes between here and the enclosing function/procedure/lambda body, then terminate the block. A local | ||
| 3013 | // variable, that is returned without a copy, is destructed by the caller | ||
| 3014 | 40855 | this.generateScopeCleanupUpTo(cast<ASTNode*>(node), this.currentScope.getFunctionScope(), node.returnedLocals.get(this.manIdx)); | |
| 3015 | 40855 | this.blockAlreadyTerminated = true; | |
| 3016 | |||
| 3017 | // Create return instruction. The cleanup code is attributed to the closing braces of the scopes, the return to this statement | ||
| 3018 | 40855 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3019 | 40855 | this.insertReturn(returnValue); | |
| 3020 | |||
| 3021 | 40855 | return Any(); | |
| 3022 | } | ||
| 3023 | |||
| 3024 | 724 | public f<Any> IRGenerator.visitBreakStmt(ASTBreakStmtNode* node) { | |
| 3025 | 724 | const BreakContinueTarget target = this.breakTargets.get(this.breakTargets.getSize() - cast<unsigned long>(node.breakTimes)); | |
| 3026 | |||
| 3027 | // Clean up all scopes between here and the loop/switch statement we are breaking out of | ||
| 3028 | 724 | this.generateScopeCleanupUpTo(cast<ASTNode*>(node), target.scope); | |
| 3029 | |||
| 3030 | // Jump to destination block. The cleanup code is attributed to the closing braces of the scopes, the jump to this statement | ||
| 3031 | 724 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3032 | 724 | this.insertJump(target.block); | |
| 3033 | |||
| 3034 | 724 | return Any(); | |
| 3035 | } | ||
| 3036 | |||
| 3037 | 1366 | public f<Any> IRGenerator.visitContinueStmt(ASTContinueStmtNode* node) { | |
| 3038 | 1366 | const BreakContinueTarget target = this.continueTargets.get(this.continueTargets.getSize() - cast<unsigned long>(node.continueTimes)); | |
| 3039 | |||
| 3040 | // Clean up all scopes between here and the loop statement we are continuing | ||
| 3041 | 1366 | this.generateScopeCleanupUpTo(cast<ASTNode*>(node), target.scope); | |
| 3042 | |||
| 3043 | // Jump to destination block. The cleanup code is attributed to the closing braces of the scopes, the jump to this statement | ||
| 3044 | 1366 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3045 | 1366 | this.insertJump(target.block); | |
| 3046 | |||
| 3047 | 1366 | return Any(); | |
| 3048 | } | ||
| 3049 | |||
| 3050 | 10 | public f<Any> IRGenerator.visitFallthroughStmt(ASTFallthroughStmtNode* node) { | |
| 3051 | // Clean up all scopes between here and the body of the case branch we are falling through from | ||
| 3052 | 10 | Scope* caseBodyScope = this.currentScope; | |
| 3053 | 13 | while caseBodyScope.scopeType != ScopeType::CASE_BODY { | |
| 3054 | 3 | caseBodyScope = caseBodyScope.parent; | |
| 3055 | 3 | } | |
| 3056 | 10 | this.generateScopeCleanupUpTo(cast<ASTNode*>(node), caseBodyScope); | |
| 3057 | |||
| 3058 | // Jump to destination block. The cleanup code is attributed to the closing braces of the scopes, the jump to this statement | ||
| 3059 | 10 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3060 | 10 | this.insertJump(this.fallthroughBlocks.top()); | |
| 3061 | |||
| 3062 | 10 | return Any(); | |
| 3063 | } | ||
| 3064 | |||
| 3065 | // Generate the body of a failed assertion | ||
| 3066 | 4787 | p IRGenerator.generateAssertBody(ASTAssertStmtNode* node) { | |
| 3067 | // Create constant for error message. It is the format string of the fprintf call below, so the condition's source text | ||
| 3068 | // must not be mistaken for conversion specifiers | ||
| 3069 | 4787 | String expressionString = node.expressionString; | |
| 3070 | 4787 | expressionString.replaceAll("%", "%%"); | |
| 3071 | 4787 | const String errorMsg = "Assertion failed: Condition '" + expressionString + "' evaluated to false.\n"; | |
| 3072 | 4787 | const String globalName = this.getUnusedGlobalName(String(ANON_GLOBAL_STRING_NAME)); | |
| 3073 | 4787 | llvm::Value globalString = this.module.createGlobalString(errorMsg, globalName.getRaw()); | |
| 3074 | // If the output should be comparable, fix alignment to 4 bytes | ||
| 3075 | 4787 | if this.cliOptions.comparableOutput { | |
| 3076 | 4787 | globalString.setAlignment(4); | |
| 3077 | 4787 | } | |
| 3078 | // Print the error message to stderr, like panic does | ||
| 3079 | 4787 | const llvm::Function fprintfFct = this.stdFunctionManager.getFPrintfFct(); | |
| 3080 | 4787 | Vector<llvm::Value> fprintfArgs; | |
| 3081 | 4787 | fprintfArgs.pushBack(this.getStdErrValue()); | |
| 3082 | 4787 | fprintfArgs.pushBack(globalString); | |
| 3083 | 4787 | this.builder.createCall(fprintfFct, fprintfArgs); | |
| 3084 | // Print the stack trace of the failed assertion | ||
| 3085 | 4787 | if this.cliOptions.printsStackTraceOnAbort() { | |
| 3086 | ✗ | Vector<llvm::Value> dumpArgs; | |
| 3087 | ✗ | dumpArgs.pushBack(this.builder.getTrue()); | |
| 3088 | ✗ | dumpArgs.pushBack(this.builder.getFalse()); | |
| 3089 | ✗ | this.builder.createCall(this.stdFunctionManager.getDumpStacktraceFct(), dumpArgs); | |
| 3090 | ✗ | } | |
| 3091 | // Generate call to exit() | ||
| 3092 | 4787 | const llvm::Function exitFct = this.stdFunctionManager.getExitFct(); | |
| 3093 | 4787 | Vector<llvm::Value> exitArgs; | |
| 3094 | 4787 | exitArgs.pushBack(this.getInt32(cast<long>(EXIT_FAILURE))); | |
| 3095 | 4787 | this.builder.createCall(exitFct, exitArgs); | |
| 3096 | // Create unreachable instruction | ||
| 3097 | 4787 | this.builder.createUnreachable(); | |
| 3098 | 4787 | this.blockAlreadyTerminated = true; | |
| 3099 | 4787 | } | |
| 3100 | |||
| 3101 | 4803 | public f<Any> IRGenerator.visitAssertStmt(ASTAssertStmtNode* node) { | |
| 3102 | // Do not generate assertions in release mode | ||
| 3103 | 4803 | if this.cliOptions.buildMode == BuildMode::RELEASE { | |
| 3104 | ✗ | return Any(); | |
| 3105 | } | ||
| 3106 | |||
| 3107 | // If we have a compile time decision, only evaluate the respective branch | ||
| 3108 | 4803 | if this.hasCompileTimeValue(node.assignExpr) { | |
| 3109 | 20 | const CompileTimeValue compileTimeValue = this.getCompileTimeValue(node.assignExpr); | |
| 3110 | 20 | if !compileTimeValue.getBoolValue() { | |
| 3111 | 4 | this.generateAssertBody(node); | |
| 3112 | 4 | } | |
| 3113 | 20 | return Any(); | |
| 3114 | } | ||
| 3115 | |||
| 3116 | // Create blocks | ||
| 3117 | 4783 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 3118 | 4783 | const llvm::BasicBlock bThen = this.createBlock("assert.then." + codeLine); | |
| 3119 | 4783 | const llvm::BasicBlock bExit = this.createBlock("assert.exit." + codeLine); | |
| 3120 | |||
| 3121 | // Visit the assignExpr | ||
| 3122 | 4783 | const llvm::Value condValue = this.resolveValue(node.assignExpr); | |
| 3123 | |||
| 3124 | // Create condition check | ||
| 3125 | 4783 | this.insertCondJump(condValue, bExit, bThen, Likelihood::LIKELY); | |
| 3126 | |||
| 3127 | // Switch to then block | ||
| 3128 | 4783 | this.switchToBlock(bThen); | |
| 3129 | 4783 | this.generateAssertBody(node); | |
| 3130 | |||
| 3131 | // Switch to exit block | ||
| 3132 | 4783 | this.switchToBlock(bExit); | |
| 3133 | |||
| 3134 | 9566 | return Any(); | |
| 3135 | 4783 | } | |
| 3136 | |||
| 3137 | // ========================================================= Expressions ========================================================= | ||
| 3138 | |||
| 3139 | 29380 | public f<Any> IRGenerator.visitAssignExpr(ASTAssignExprNode* node) { | |
| 3140 | // Visit ternary expression | ||
| 3141 | 29380 | if node.ternaryExpr != nil<IVisitable*> { | |
| 3142 | ✗ | return this.visit(node.ternaryExpr); | |
| 3143 | } | ||
| 3144 | |||
| 3145 | 29380 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3146 | |||
| 3147 | // Assign or compound assign operation | ||
| 3148 | 29380 | if node.op != AssignOp::OP_NONE { | |
| 3149 | 29380 | IVisitable* lhsNode = node.lhs; | |
| 3150 | 29380 | IVisitable* rhsNode = node.rhs; | |
| 3151 | 29380 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 3152 | |||
| 3153 | // Normal assignment | ||
| 3154 | 29380 | if node.op == AssignOp::OP_ASSIGN { | |
| 3155 | 26703 | return exprResultAny(this.doAssignment(lhsNode, rhsNode, baseNode)); | |
| 3156 | } | ||
| 3157 | |||
| 3158 | // Compound assignment | ||
| 3159 | // Get symbol types of left and right side | ||
| 3160 | 2677 | const QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3161 | 2677 | const QualType rhsSTy = this.getEvaluatedType(rhsNode); | |
| 3162 | |||
| 3163 | // Retrieve rhs | ||
| 3164 | 2677 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 3165 | // Retrieve lhs | ||
| 3166 | 2677 | LLVMExprResult lhs = this.visitExpr(lhsNode); | |
| 3167 | |||
| 3168 | 2677 | LLVMExprResult res; | |
| 3169 | 2677 | switch node.op { | |
| 3170 | 1917 | case AssignOp::OP_PLUS_EQUAL: { res = this.conversionManager.getPlusEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3171 | 92 | case AssignOp::OP_MINUS_EQUAL: { res = this.conversionManager.getMinusEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3172 | 159 | case AssignOp::OP_MUL_EQUAL: { res = this.conversionManager.getMulEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3173 | 106 | case AssignOp::OP_DIV_EQUAL: { res = this.conversionManager.getDivEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3174 | 61 | case AssignOp::OP_REM_EQUAL: { res = this.conversionManager.getRemEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3175 | 12 | case AssignOp::OP_SHL_EQUAL: { res = this.conversionManager.getSHLEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3176 | 19 | case AssignOp::OP_SHR_EQUAL: { res = this.conversionManager.getSHREqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3177 | 30 | case AssignOp::OP_AND_EQUAL: { res = this.conversionManager.getAndEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3178 | 32 | case AssignOp::OP_OR_EQUAL: { res = this.conversionManager.getOrEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3179 | 249 | case AssignOp::OP_XOR_EQUAL: { res = this.conversionManager.getXorEqualInst(baseNode, lhs, lhsSTy, rhs, rhsSTy); } | |
| 3180 | − | default: { panic(Error("Assign op fall-through")); } // LCOV_EXCL_LINE | |
| 3181 | } | ||
| 3182 | |||
| 3183 | 2677 | if !res.ptr.isNull() { // The operation allocated more memory | |
| 3184 | ✗ | if lhs.entry != nil<const SymbolTableEntry*> { | |
| 3185 | ✗ | this.updateAddress(lhs.entry, res.ptr); | |
| 3186 | ✗ | } | |
| 3187 | 2677 | } else if !res.value.isNull() { // The operation only updated the value | |
| 3188 | // Store the result | ||
| 3189 | 1832 | lhs.value = res.value; | |
| 3190 | 1832 | this.insertStore(lhs.value, lhs.ptr, lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile); | |
| 3191 | 2677 | } | |
| 3192 | 2677 | return exprResultAny(lhs); | |
| 3193 | } | ||
| 3194 | |||
| 3195 | // This is a fallthrough case -> throw an error | ||
| 3196 | − | panic(Error("AssignStmt fall-through")); // LCOV_EXCL_LINE | |
| 3197 | } | ||
| 3198 | |||
| 3199 | // Enter the expression scope of the given expression, if it has one | ||
| 3200 | 3688 | f<Scope*> IRGenerator.enterExprScope(IVisitable* expr) { | |
| 3201 | 3688 | Scope* exprScope = this.currentScope.getChildScope(getExprScopeId(expr)); | |
| 3202 | 3688 | if exprScope != nil<Scope*> { | |
| 3203 | 3688 | this.compilerPass.changeToScope(exprScope, ScopeType::EXPR_BODY); | |
| 3204 | 3688 | } | |
| 3205 | 7376 | return exprScope; | |
| 3206 | 3688 | } | |
| 3207 | |||
| 3208 | // Leave the given expression scope, if it exists | ||
| 3209 | 3690 | p IRGenerator.leaveExprScope(Scope* exprScope) { | |
| 3210 | 3690 | if exprScope != nil<Scope*> { | |
| 3211 | 3688 | this.compilerPass.changeToParentScope(ScopeType::EXPR_BODY); | |
| 3212 | 3688 | } | |
| 3213 | 3690 | } | |
| 3214 | |||
| 3215 | 2206 | public f<Any> IRGenerator.visitTernaryExpr(ASTTernaryExprNode* node) { | |
| 3216 | // Check if only one operand is present -> loop through | ||
| 3217 | 2206 | if node.falseExpr == nil<IVisitable*> { | |
| 3218 | ✗ | return this.visit(node.condition); | |
| 3219 | } | ||
| 3220 | |||
| 3221 | 2206 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3222 | |||
| 3223 | // It is a ternary | ||
| 3224 | // Retrieve the condition value | ||
| 3225 | 2206 | const llvm::Value condValue = this.resolveValue(node.condition); | |
| 3226 | 2206 | IVisitable* trueNode = node.isShortened ? node.condition : node.trueExpr; | |
| 3227 | 2206 | IVisitable* falseNode = node.falseExpr; | |
| 3228 | 2206 | const TernaryExprData& data = node.data.get(this.manIdx); | |
| 3229 | |||
| 3230 | 2206 | llvm::Value resultValue; | |
| 3231 | 2206 | llvm::Value resultPtr; | |
| 3232 | 2206 | const SymbolTableEntry* anonymousSymbol = nil<const SymbolTableEntry*>; | |
| 3233 | 2206 | if this.hasCompileTimeValue(trueNode) && this.hasCompileTimeValue(falseNode) { | |
| 3234 | // If both are constants, we can simply emit a selection instruction | ||
| 3235 | 361 | const llvm::Value trueValue = this.resolveValue(trueNode); | |
| 3236 | 361 | const llvm::Value falseValue = this.resolveValue(falseNode); | |
| 3237 | 361 | resultValue = this.builder.createSelect(condValue, trueValue, falseValue); | |
| 3238 | 361 | } else { | |
| 3239 | // We have at least one non-constant value, use branching to not perform both sides | ||
| 3240 | 1845 | const String codeLoc = node.codeLoc.toPrettyLineAndColumn(); | |
| 3241 | 1845 | llvm::BasicBlock condTrue = this.createBlock("cond.true." + codeLoc); | |
| 3242 | 1845 | llvm::BasicBlock condFalse = this.createBlock("cond.false." + codeLoc); | |
| 3243 | 1845 | const llvm::BasicBlock condExit = this.createBlock("cond.exit." + codeLoc); | |
| 3244 | |||
| 3245 | // Jump from original block to true or false block, depending on condition | ||
| 3246 | 1845 | this.insertCondJump(condValue, condTrue, condFalse); | |
| 3247 | |||
| 3248 | // Fill true block | ||
| 3249 | 1845 | this.switchToBlock(condTrue); | |
| 3250 | 1845 | const QualType resultType = this.getEvaluatedType(cast<IVisitable*>(node)); | |
| 3251 | 1845 | llvm::Value trueValue; | |
| 3252 | 1845 | llvm::Value truePtr; | |
| 3253 | // The true branch has an expression scope for its temporaries, except for shortened ternaries (the condition is the branch) | ||
| 3254 | 1845 | Scope* trueScope = nil<Scope*>; | |
| 3255 | 1845 | if !node.isShortened { | |
| 3256 | 1843 | trueScope = this.enterExprScope(trueNode); | |
| 3257 | 1843 | } | |
| 3258 | 1845 | if data.falseSideCallsCopyCtor || resultType.isRef() { // both sides or only the false side needs copy ctor call | |
| 3259 | 122 | truePtr = this.resolveAddress(trueNode); | |
| 3260 | 1845 | } else if data.trueSideCallsCopyCtor { // only true side needs copy ctor call | |
| 3261 | 9 | const llvm::Value originalPtr = this.resolveAddress(trueNode); | |
| 3262 | // Allocate storage for the copy using the ternary's own result type, not trueNode's own evaluated type: | ||
| 3263 | // trueNode may be a reference (e.g. a 'const T&' parameter), whose LLVM type is just a pointer and would | ||
| 3264 | // undersize this alloca for the struct value the copy ctor is about to write into it. | ||
| 3265 | 9 | truePtr = this.insertAlloca(resultType); | |
| 3266 | 9 | Vector<llvm::Value> ctorArgs; | |
| 3267 | 9 | ctorArgs.pushBack(originalPtr); | |
| 3268 | 9 | this.generateCtorOrDtorCall(truePtr, data.calledCopyCtor, ctorArgs); | |
| 3269 | 9 | } else { // neither true nor false side need copy ctor call | |
| 3270 | 1714 | trueValue = this.resolveValue(trueNode); | |
| 3271 | 1723 | } | |
| 3272 | 1845 | this.leaveExprScope(trueScope); | |
| 3273 | // Set the true block to the current insert point, since it could have changed in the meantime | ||
| 3274 | 1845 | condTrue = this.builder.getInsertBlock(); | |
| 3275 | 1845 | this.insertJump(condExit); | |
| 3276 | |||
| 3277 | // Fill false block | ||
| 3278 | 1845 | this.switchToBlock(condFalse); | |
| 3279 | 1845 | llvm::Value falseValue; | |
| 3280 | 1845 | llvm::Value falsePtr; | |
| 3281 | // The false branch has an expression scope for its temporaries | ||
| 3282 | 1845 | Scope* falseScope = this.enterExprScope(falseNode); | |
| 3283 | 1845 | if data.trueSideCallsCopyCtor || resultType.isRef() { // both sides or only the true side needs copy ctor call | |
| 3284 | 114 | falsePtr = this.resolveAddress(falseNode); | |
| 3285 | 1845 | } else if data.falseSideCallsCopyCtor { // only false side needs copy ctor call | |
| 3286 | 17 | const llvm::Value originalPtr = this.resolveAddress(falseNode); | |
| 3287 | // See the analogous truePtr allocation above: use the ternary's result type, not falseNode's own | ||
| 3288 | // (possibly reference-qualified) evaluated type. | ||
| 3289 | 17 | falsePtr = this.insertAlloca(resultType); | |
| 3290 | 17 | Vector<llvm::Value> ctorArgs; | |
| 3291 | 17 | ctorArgs.pushBack(originalPtr); | |
| 3292 | 17 | this.generateCtorOrDtorCall(falsePtr, data.calledCopyCtor, ctorArgs); | |
| 3293 | 17 | } else { // neither true nor false side need copy ctor call | |
| 3294 | 1714 | falseValue = this.resolveValue(falseNode); | |
| 3295 | 1731 | } | |
| 3296 | 1845 | this.leaveExprScope(falseScope); | |
| 3297 | // Set the true block to the current insert point, since it could have changed in the meantime | ||
| 3298 | 1845 | condFalse = this.builder.getInsertBlock(); | |
| 3299 | 1845 | this.insertJump(condExit); | |
| 3300 | |||
| 3301 | // Fill the exit block | ||
| 3302 | 1845 | this.switchToBlock(condExit); | |
| 3303 | 1845 | if data.trueSideCallsCopyCtor || data.falseSideCallsCopyCtor || resultType.isRef() { // one side calls copy ctor | |
| 3304 | 131 | llvm::Value phiInst = this.builder.createPhi(this.builder.getPtrTy(), "cond.result"); | |
| 3305 | 131 | phiInst.addIncoming(truePtr, condTrue); | |
| 3306 | 131 | phiInst.addIncoming(falsePtr, condFalse); | |
| 3307 | 131 | if data.trueSideCallsCopyCtor && data.falseSideCallsCopyCtor { // both sides need copy ctor call | |
| 3308 | ✗ | resultPtr = this.insertAlloca(resultType); | |
| 3309 | ✗ | Vector<llvm::Value> ctorArgs; | |
| 3310 | ✗ | ctorArgs.pushBack(phiInst); | |
| 3311 | ✗ | this.generateCtorOrDtorCall(resultPtr, data.calledCopyCtor, ctorArgs); | |
| 3312 | ✗ | } else { | |
| 3313 | 131 | resultPtr = phiInst; | |
| 3314 | 131 | } | |
| 3315 | 131 | } else { // neither true nor false side calls copy ctor | |
| 3316 | 1714 | assert !trueValue.isNull(); | |
| 3317 | 1714 | llvm::Value phiInst = this.builder.createPhi(resultType.toLLVMType(this.sourceFile), "cond.result"); | |
| 3318 | 1714 | phiInst.addIncoming(trueValue, condTrue); | |
| 3319 | 1714 | phiInst.addIncoming(falseValue, condFalse); | |
| 3320 | 1714 | resultValue = phiInst; | |
| 3321 | 1714 | } | |
| 3322 | |||
| 3323 | // If we have an anonymous symbol for this ternary expr, make sure that it has an address to reference. | ||
| 3324 | 1845 | anonymousSymbol = this.currentScope.symbolTable.lookupAnonymous(cast<ASTNode*>(node)); | |
| 3325 | 1845 | if anonymousSymbol != nil<const SymbolTableEntry*> { | |
| 3326 | 41 | if resultPtr.isNull() { | |
| 3327 | 2 | resultPtr = this.insertAlloca(anonymousSymbol.getQualType()); | |
| 3328 | 2 | this.insertStore(resultValue, resultPtr); | |
| 3329 | 2 | } | |
| 3330 | 41 | this.updateAddress(anonymousSymbol, resultPtr); | |
| 3331 | 41 | } | |
| 3332 | |||
| 3333 | // Destruct the temporaries of the branch that was evaluated. This must happen here, after the result was copied out of the | ||
| 3334 | // branch and only for the branch that was taken, since the temporaries of the other one were never constructed. | ||
| 3335 | 1845 | const bool trueNeedsCleanup = trueScope != nil<Scope*> && !trueScope.temporaryDtorsToCall.isEmpty(); | |
| 3336 | 1845 | const bool falseNeedsCleanup = falseScope != nil<Scope*> && !falseScope.temporaryDtorsToCall.isEmpty(); | |
| 3337 | 1845 | if trueNeedsCleanup || falseNeedsCleanup { | |
| 3338 | 3 | const llvm::BasicBlock bCleanTrue = this.createBlock("cond.cleanup.true." + codeLoc); | |
| 3339 | 3 | const llvm::BasicBlock bCleanFalse = this.createBlock("cond.cleanup.false." + codeLoc); | |
| 3340 | 3 | const llvm::BasicBlock bCleanExit = this.createBlock("cond.cleanup.exit." + codeLoc); | |
| 3341 | 3 | this.insertCondJump(condValue, bCleanTrue, bCleanFalse); | |
| 3342 | |||
| 3343 | 3 | this.switchToBlock(bCleanTrue); | |
| 3344 | 3 | if trueNeedsCleanup { | |
| 3345 | 2 | this.generateTemporariesCleanup(trueScope, getASTNode(trueNode)); | |
| 3346 | 2 | } | |
| 3347 | 3 | this.insertJump(bCleanExit); | |
| 3348 | |||
| 3349 | 3 | this.switchToBlock(bCleanFalse); | |
| 3350 | 3 | if falseNeedsCleanup { | |
| 3351 | 1 | this.generateTemporariesCleanup(falseScope, getASTNode(falseNode)); | |
| 3352 | 1 | } | |
| 3353 | 3 | this.insertJump(bCleanExit); | |
| 3354 | |||
| 3355 | 3 | this.switchToBlock(bCleanExit); | |
| 3356 | 3 | } | |
| 3357 | 1845 | } | |
| 3358 | |||
| 3359 | 2206 | LLVMExprResult res; | |
| 3360 | 2206 | res.value = resultValue; | |
| 3361 | 2206 | res.ptr = resultPtr; | |
| 3362 | 2206 | res.entry = anonymousSymbol; | |
| 3363 | 2206 | return exprResultAny(res); | |
| 3364 | } | ||
| 3365 | |||
| 3366 | // Shared implementation of visitLogicalOrExpr and visitLogicalAndExpr | ||
| 3367 | 5307 | f<Any> IRGenerator.visitLogicalExpr(ASTNode* node, const Vector<IVisitable*>& operands, bool isOr) { | |
| 3368 | 5307 | this.diGenerator.setSourceLocation(node); | |
| 3369 | |||
| 3370 | // Create exit block for short-circuiting | ||
| 3371 | 5307 | const String codeLoc = node.codeLoc.toPrettyLineAndColumn(); | |
| 3372 | 5307 | const String prefix = String(isOr ? "lor" : "land"); | |
| 3373 | 5307 | const llvm::BasicBlock bExit = this.createBlock(prefix + ".exit." + codeLoc); | |
| 3374 | |||
| 3375 | // Visit the first operand | ||
| 3376 | 5307 | const llvm::Value firstOperandValue = this.resolveValue(operands.front()); | |
| 3377 | |||
| 3378 | // Prepare an array for value-to-block-mapping | ||
| 3379 | 5307 | Vector<llvm::BasicBlock> shortCircuitBlocks; | |
| 3380 | 5307 | Vector<llvm::Value> shortCircuitValues; | |
| 3381 | 5307 | shortCircuitBlocks.reserve(operands.getSize()); | |
| 3382 | 5307 | shortCircuitValues.reserve(operands.getSize()); | |
| 3383 | // The first element is the first operand value with the original block | ||
| 3384 | 5307 | shortCircuitBlocks.pushBack(this.builder.getInsertBlock()); | |
| 3385 | 5307 | shortCircuitValues.pushBack(firstOperandValue); | |
| 3386 | // Create a block for each additional operand and save it to the mapping | ||
| 3387 | 11803 | for unsigned long i = 1l; i < operands.getSize(); i++ { | |
| 3388 | 6496 | shortCircuitBlocks.pushBack(this.createBlock(prefix + "." + toString(i) + "." + codeLoc)); | |
| 3389 | 6496 | shortCircuitValues.pushBack(llvm::Value()); | |
| 3390 | 6496 | } | |
| 3391 | // Create conditional jump to the exit block if the first operand was true (or), otherwise to the next block | ||
| 3392 | 5307 | if isOr { | |
| 3393 | 3375 | this.insertCondJump(firstOperandValue, bExit, shortCircuitBlocks.get(1l)); | |
| 3394 | 3375 | } else { | |
| 3395 | 1932 | this.insertCondJump(firstOperandValue, shortCircuitBlocks.get(1l), bExit); | |
| 3396 | 1932 | } | |
| 3397 | |||
| 3398 | // Create block for each operand | ||
| 3399 | 11803 | for unsigned long i = 1l; i < operands.getSize(); i++ { | |
| 3400 | // Switch to the next block | ||
| 3401 | 6496 | this.switchToBlock(shortCircuitBlocks.get(i)); | |
| 3402 | // Evaluate operand and save the result in the mapping. The temporaries of the operand are destructed within its block, since | ||
| 3403 | // they only exist if the operand was evaluated | ||
| 3404 | 6496 | llvm::Value& operandValue = shortCircuitValues.get(i); | |
| 3405 | 6496 | operandValue = this.resolveValueInExprScope(operands.get(i)); | |
| 3406 | // Replace the array entry with the current insert block, since the insert block could have changed in the meantime | ||
| 3407 | 6496 | llvm::BasicBlock& operandBlock = shortCircuitBlocks.get(i); | |
| 3408 | 6496 | operandBlock = this.builder.getInsertBlock(); | |
| 3409 | // Check if there are more blocks to process | ||
| 3410 | 6496 | if i == operands.getSize() - 1l { | |
| 3411 | // Insert a simple jump to the exit block for the last block | ||
| 3412 | 5307 | this.insertJump(bExit); | |
| 3413 | 6496 | } else if isOr { | |
| 3414 | // Create conditional jump to the exit block if the operand was true, otherwise to the next block | ||
| 3415 | 954 | this.insertCondJump(operandValue, bExit, shortCircuitBlocks.get(i + 1l)); | |
| 3416 | 954 | } else { | |
| 3417 | // Create conditional jump to the next block if the operand was true, otherwise to the exit block | ||
| 3418 | 235 | this.insertCondJump(operandValue, shortCircuitBlocks.get(i + 1l), bExit); | |
| 3419 | 1189 | } | |
| 3420 | 6496 | } | |
| 3421 | |||
| 3422 | // Get the result with the phi node | ||
| 3423 | 5307 | this.switchToBlock(bExit); | |
| 3424 | 5307 | llvm::Value res = this.builder.createPhi(firstOperandValue.getType(), isOr ? "lor_phi" : "land_phi"); | |
| 3425 | 17110 | for unsigned long i = 0l; i < shortCircuitBlocks.getSize(); i++ { | |
| 3426 | 11803 | res.addIncoming(shortCircuitValues.get(i), shortCircuitBlocks.get(i)); | |
| 3427 | 11803 | } | |
| 3428 | |||
| 3429 | // Return the result | ||
| 3430 | 10614 | return exprResultAny(exprResultWithValue(res)); | |
| 3431 | 5307 | } | |
| 3432 | |||
| 3433 | 3375 | public f<Any> IRGenerator.visitLogicalOrExpr(ASTLogicalOrExprNode* node) { | |
| 3434 | // Check if only one operand is present -> loop through | ||
| 3435 | 3375 | if node.operands.getSize() == 1l { | |
| 3436 | ✗ | return this.visit(node.operands.front()); | |
| 3437 | } | ||
| 3438 | |||
| 3439 | // It is a logical or expression | ||
| 3440 | 3375 | return this.visitLogicalExpr(cast<ASTNode*>(node), node.operands, true); | |
| 3441 | } | ||
| 3442 | |||
| 3443 | 1932 | public f<Any> IRGenerator.visitLogicalAndExpr(ASTLogicalAndExprNode* node) { | |
| 3444 | // Check if only one operand is present -> loop through | ||
| 3445 | 1932 | if node.operands.getSize() == 1l { | |
| 3446 | ✗ | return this.visit(node.operands.front()); | |
| 3447 | } | ||
| 3448 | |||
| 3449 | // It is a logical and expression | ||
| 3450 | 1932 | return this.visitLogicalExpr(cast<ASTNode*>(node), node.operands, false); | |
| 3451 | } | ||
| 3452 | |||
| 3453 | // Kinds of bitwise expressions | ||
| 3454 | type BitwiseExprKind enum { | ||
| 3455 | OR, | ||
| 3456 | XOR, | ||
| 3457 | AND | ||
| 3458 | } | ||
| 3459 | |||
| 3460 | // Shared implementation of visitBitwiseOrExpr, visitBitwiseXorExpr and visitBitwiseAndExpr | ||
| 3461 | 808 | f<Any> IRGenerator.visitBitwiseExpr(ASTNode* node, const Vector<IVisitable*>& operands, BitwiseExprKind kind) { | |
| 3462 | 808 | this.diGenerator.setSourceLocation(node); | |
| 3463 | |||
| 3464 | // Evaluate first operand | ||
| 3465 | 808 | IVisitable* lhsNode = operands.front(); | |
| 3466 | 808 | const QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3467 | 808 | LLVMExprResult res = this.visitExpr(lhsNode); | |
| 3468 | |||
| 3469 | // Evaluate all additional operands | ||
| 3470 | 1624 | for unsigned long i = 1l; i < operands.getSize(); i++ { | |
| 3471 | // Evaluate the operand | ||
| 3472 | 816 | IVisitable* rhsNode = operands.get(i); | |
| 3473 | 816 | const QualType rhsSTy = this.getEvaluatedType(rhsNode); | |
| 3474 | 816 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 3475 | 816 | switch kind { | |
| 3476 | 429 | case BitwiseExprKind::OR: { res = this.conversionManager.getBitwiseOrInst(node, res, lhsSTy, rhs, rhsSTy, i - 1l); } | |
| 3477 | 82 | case BitwiseExprKind::XOR: { res = this.conversionManager.getBitwiseXorInst(node, res, lhsSTy, rhs, rhsSTy, i - 1l); } | |
| 3478 | 305 | case BitwiseExprKind::AND: { res = this.conversionManager.getBitwiseAndInst(node, res, lhsSTy, rhs, rhsSTy, i - 1l); } | |
| 3479 | } | ||
| 3480 | 816 | } | |
| 3481 | |||
| 3482 | // Return result | ||
| 3483 | 808 | return exprResultAny(res); | |
| 3484 | } | ||
| 3485 | |||
| 3486 | 425 | public f<Any> IRGenerator.visitBitwiseOrExpr(ASTBitwiseOrExprNode* node) { | |
| 3487 | // Check if only one operand is present -> loop through | ||
| 3488 | 425 | if node.operands.getSize() == 1l { | |
| 3489 | ✗ | return this.visit(node.operands.front()); | |
| 3490 | } | ||
| 3491 | // It is a bitwise or expression | ||
| 3492 | 425 | return this.visitBitwiseExpr(cast<ASTNode*>(node), node.operands, BitwiseExprKind::OR); | |
| 3493 | } | ||
| 3494 | |||
| 3495 | 80 | public f<Any> IRGenerator.visitBitwiseXorExpr(ASTBitwiseXorExprNode* node) { | |
| 3496 | // Check if only one operand is present -> loop through | ||
| 3497 | 80 | if node.operands.getSize() == 1l { | |
| 3498 | ✗ | return this.visit(node.operands.front()); | |
| 3499 | } | ||
| 3500 | // It is a bitwise xor expression | ||
| 3501 | 80 | return this.visitBitwiseExpr(cast<ASTNode*>(node), node.operands, BitwiseExprKind::XOR); | |
| 3502 | } | ||
| 3503 | |||
| 3504 | 303 | public f<Any> IRGenerator.visitBitwiseAndExpr(ASTBitwiseAndExprNode* node) { | |
| 3505 | // Check if only one operand is present -> loop through | ||
| 3506 | 303 | if node.operands.getSize() == 1l { | |
| 3507 | ✗ | return this.visit(node.operands.front()); | |
| 3508 | } | ||
| 3509 | // It is a bitwise and expression | ||
| 3510 | 303 | return this.visitBitwiseExpr(cast<ASTNode*>(node), node.operands, BitwiseExprKind::AND); | |
| 3511 | } | ||
| 3512 | |||
| 3513 | 27520 | public f<Any> IRGenerator.visitEqualityExpr(ASTEqualityExprNode* node) { | |
| 3514 | // Check if only one operand is present -> loop through | ||
| 3515 | 27520 | if node.operands.getSize() == 1l { | |
| 3516 | ✗ | return this.visit(node.operands.front()); | |
| 3517 | } | ||
| 3518 | |||
| 3519 | 27520 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3520 | |||
| 3521 | // It is an equality expression | ||
| 3522 | // Evaluate lhs | ||
| 3523 | 27520 | IVisitable* lhsNode = node.operands.get(0l); | |
| 3524 | 27520 | const QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3525 | 27520 | LLVMExprResult res = this.visitExpr(lhsNode); | |
| 3526 | |||
| 3527 | // Evaluate rhs | ||
| 3528 | 27520 | IVisitable* rhsNode = node.operands.get(1l); | |
| 3529 | 27520 | const QualType rhsSTy = this.getEvaluatedType(rhsNode); | |
| 3530 | 27520 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 3531 | |||
| 3532 | // Retrieve the result value, based on the exact operator | ||
| 3533 | 27520 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 3534 | 27520 | switch node.op { | |
| 3535 | 22765 | case EqualityOp::OP_EQUAL: { res = this.conversionManager.getEqualInst(baseNode, res, lhsSTy, rhs, rhsSTy); } | |
| 3536 | 4755 | case EqualityOp::OP_NOT_EQUAL: { res = this.conversionManager.getNotEqualInst(baseNode, res, lhsSTy, rhs, rhsSTy); } | |
| 3537 | − | default: { panic(Error("EqualityExpr fall-through")); } // LCOV_EXCL_LINE | |
| 3538 | } | ||
| 3539 | |||
| 3540 | // Return the result | ||
| 3541 | 27520 | return exprResultAny(res); | |
| 3542 | } | ||
| 3543 | |||
| 3544 | 16919 | public f<Any> IRGenerator.visitRelationalExpr(ASTRelationalExprNode* node) { | |
| 3545 | // Check if only one operand is present -> loop through | ||
| 3546 | 16919 | if node.operands.getSize() == 1l { | |
| 3547 | ✗ | return this.visit(node.operands.front()); | |
| 3548 | } | ||
| 3549 | |||
| 3550 | 16919 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3551 | |||
| 3552 | // It is a relational expression | ||
| 3553 | // Evaluate lhs | ||
| 3554 | 16919 | IVisitable* lhsNode = node.operands.get(0l); | |
| 3555 | 16919 | const QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3556 | 16919 | LLVMExprResult res = this.visitExpr(lhsNode); | |
| 3557 | |||
| 3558 | // Evaluate rhs | ||
| 3559 | 16919 | IVisitable* rhsNode = node.operands.get(1l); | |
| 3560 | 16919 | const QualType rhsSTy = this.getEvaluatedType(rhsNode); | |
| 3561 | 16919 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 3562 | |||
| 3563 | // Retrieve the result value, based on the exact operator | ||
| 3564 | 16919 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 3565 | 16919 | switch node.op { | |
| 3566 | 6953 | case RelationalOp::OP_LESS: { res = this.conversionManager.getLessInst(baseNode, res, lhsSTy, rhs, rhsSTy); } | |
| 3567 | 3636 | case RelationalOp::OP_GREATER: { res = this.conversionManager.getGreaterInst(baseNode, res, lhsSTy, rhs, rhsSTy); } | |
| 3568 | 2181 | case RelationalOp::OP_LESS_EQUAL: { res = this.conversionManager.getLessEqualInst(baseNode, res, lhsSTy, rhs, rhsSTy); } | |
| 3569 | 4149 | case RelationalOp::OP_GREATER_EQUAL: { res = this.conversionManager.getGreaterEqualInst(baseNode, res, lhsSTy, rhs, rhsSTy); } | |
| 3570 | − | default: { panic(Error("EqualityExpr fall-through")); } // LCOV_EXCL_LINE | |
| 3571 | } | ||
| 3572 | |||
| 3573 | // Return the result | ||
| 3574 | 16919 | return exprResultAny(res); | |
| 3575 | } | ||
| 3576 | |||
| 3577 | 753 | public f<Any> IRGenerator.visitShiftExpr(ASTShiftExprNode* node) { | |
| 3578 | // Check if only one operand is present -> loop through | ||
| 3579 | 753 | if node.operands.getSize() == 1l { | |
| 3580 | ✗ | return this.visit(node.operands.front()); | |
| 3581 | } | ||
| 3582 | |||
| 3583 | 753 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3584 | |||
| 3585 | // It is a shift expression | ||
| 3586 | // Evaluate first operand | ||
| 3587 | 753 | IVisitable* lhsNode = node.operands.front(); | |
| 3588 | 753 | QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3589 | 753 | LLVMExprResult lhs = this.visitExpr(lhsNode); | |
| 3590 | |||
| 3591 | 753 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 3592 | 753 | ShiftOpQueue opQueue = node.opQueue; | |
| 3593 | 753 | unsigned long operandIndex = 1l; | |
| 3594 | 1739 | while !opQueue.isEmpty() { | |
| 3595 | 986 | const unsigned long operatorIndex = operandIndex - 1l; | |
| 3596 | // Evaluate next operand | ||
| 3597 | 986 | IVisitable* rhsNode = node.operands.get(operandIndex); | |
| 3598 | 986 | operandIndex++; | |
| 3599 | 986 | assert rhsNode != nil<IVisitable*>; | |
| 3600 | 986 | const QualType rhsSTy = this.getEvaluatedType(rhsNode); | |
| 3601 | 986 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 3602 | |||
| 3603 | // Retrieve the result, based on the exact operator | ||
| 3604 | 986 | const Pair<ShiftOp, QualType>& opPair = opQueue.front(); | |
| 3605 | 986 | const ShiftOp currentOp = opPair.getFirst(); | |
| 3606 | 986 | switch currentOp { | |
| 3607 | 666 | case ShiftOp::OP_SHIFT_LEFT: { lhs = this.conversionManager.getShiftLeftInst(baseNode, lhs, lhsSTy, rhs, rhsSTy, operatorIndex); } | |
| 3608 | 320 | case ShiftOp::OP_SHIFT_RIGHT: { lhs = this.conversionManager.getShiftRightInst(baseNode, lhs, lhsSTy, rhs, rhsSTy, operatorIndex); } | |
| 3609 | − | default: { panic(Error("AdditiveExpr fall-through")); } // LCOV_EXCL_LINE | |
| 3610 | } | ||
| 3611 | |||
| 3612 | // Retrieve the new lhs symbol type | ||
| 3613 | 986 | lhsSTy = opPair.getSecond(); | |
| 3614 | |||
| 3615 | 986 | opQueue.pop(); | |
| 3616 | 986 | } | |
| 3617 | |||
| 3618 | // Return the result | ||
| 3619 | 1506 | return exprResultAny(lhs); | |
| 3620 | 753 | } | |
| 3621 | |||
| 3622 | 15404 | public f<Any> IRGenerator.visitAdditiveExpr(ASTAdditiveExprNode* node) { | |
| 3623 | // Check if only one operand is present -> loop through | ||
| 3624 | 15404 | if node.operands.getSize() == 1l { | |
| 3625 | ✗ | return this.visit(node.operands.front()); | |
| 3626 | } | ||
| 3627 | |||
| 3628 | 15404 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3629 | |||
| 3630 | // It is an additive expression | ||
| 3631 | // Evaluate first operand | ||
| 3632 | 15404 | IVisitable* lhsNode = node.operands.get(0l); | |
| 3633 | 15404 | QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3634 | 15404 | LLVMExprResult lhs = this.visitExpr(lhsNode); | |
| 3635 | |||
| 3636 | 15404 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 3637 | 15404 | AdditiveOpQueue opQueue = node.opQueue; | |
| 3638 | 15404 | unsigned long operandIndex = 1l; | |
| 3639 | 32487 | while !opQueue.isEmpty() { | |
| 3640 | 17083 | const unsigned long operatorIndex = operandIndex - 1l; | |
| 3641 | // Evaluate next operand | ||
| 3642 | 17083 | IVisitable* rhsNode = node.operands.get(operandIndex); | |
| 3643 | 17083 | operandIndex++; | |
| 3644 | 17083 | assert rhsNode != nil<IVisitable*>; | |
| 3645 | 17083 | const QualType rhsSTy = this.getEvaluatedType(rhsNode); | |
| 3646 | 17083 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 3647 | |||
| 3648 | // Retrieve the result, based on the exact operator | ||
| 3649 | 17083 | const Pair<AdditiveOp, QualType>& opPair = opQueue.front(); | |
| 3650 | 17083 | const AdditiveOp currentOp = opPair.getFirst(); | |
| 3651 | 17083 | switch currentOp { | |
| 3652 | 10363 | case AdditiveOp::OP_PLUS: { lhs = this.conversionManager.getPlusInst(baseNode, lhs, lhsSTy, rhs, rhsSTy, operatorIndex); } | |
| 3653 | 6720 | case AdditiveOp::OP_MINUS: { lhs = this.conversionManager.getMinusInst(baseNode, lhs, lhsSTy, rhs, rhsSTy, operatorIndex); } | |
| 3654 | − | default: { panic(Error("AdditiveExpr fall-through")); } // LCOV_EXCL_LINE | |
| 3655 | } | ||
| 3656 | |||
| 3657 | // Retrieve the new lhs symbol type | ||
| 3658 | 17083 | lhsSTy = opPair.getSecond(); | |
| 3659 | |||
| 3660 | 17083 | opQueue.pop(); | |
| 3661 | 17083 | } | |
| 3662 | |||
| 3663 | // Return the result | ||
| 3664 | 30808 | return exprResultAny(lhs); | |
| 3665 | 15404 | } | |
| 3666 | |||
| 3667 | 2867 | public f<Any> IRGenerator.visitMultiplicativeExpr(ASTMultiplicativeExprNode* node) { | |
| 3668 | // Check if only one operand is present -> loop through | ||
| 3669 | 2867 | if node.operands.getSize() == 1l { | |
| 3670 | ✗ | return this.visit(node.operands.front()); | |
| 3671 | } | ||
| 3672 | |||
| 3673 | 2867 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3674 | |||
| 3675 | // It is an additive expression | ||
| 3676 | // Evaluate first operand | ||
| 3677 | 2867 | IVisitable* lhsNode = node.operands.get(0l); | |
| 3678 | 2867 | QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3679 | 2867 | LLVMExprResult res = this.visitExpr(lhsNode); | |
| 3680 | |||
| 3681 | 2867 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 3682 | 2867 | MultiplicativeOpQueue opQueue = node.opQueue; | |
| 3683 | 2867 | unsigned long operandIndex = 1l; | |
| 3684 | 5824 | while !opQueue.isEmpty() { | |
| 3685 | 2957 | const unsigned long operatorIndex = operandIndex - 1l; | |
| 3686 | // Evaluate next operand | ||
| 3687 | 2957 | IVisitable* rhsNode = node.operands.get(operandIndex); | |
| 3688 | 2957 | operandIndex++; | |
| 3689 | 2957 | assert rhsNode != nil<IVisitable*>; | |
| 3690 | 2957 | const QualType rhsSTy = this.getEvaluatedType(rhsNode); | |
| 3691 | 2957 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 3692 | |||
| 3693 | // Retrieve the result, based on the exact operator | ||
| 3694 | 2957 | const Pair<MultiplicativeOp, QualType>& opPair = opQueue.front(); | |
| 3695 | 2957 | const MultiplicativeOp currentOp = opPair.getFirst(); | |
| 3696 | 2957 | switch currentOp { | |
| 3697 | 2409 | case MultiplicativeOp::OP_MUL: { res = this.conversionManager.getMulInst(baseNode, res, lhsSTy, rhs, rhsSTy, operatorIndex); } | |
| 3698 | 339 | case MultiplicativeOp::OP_DIV: { res = this.conversionManager.getDivInst(baseNode, res, lhsSTy, rhs, rhsSTy, operatorIndex); } | |
| 3699 | 209 | case MultiplicativeOp::OP_REM: { res = this.conversionManager.getRemInst(baseNode, res, lhsSTy, rhs, rhsSTy); } | |
| 3700 | − | default: { panic(Error("MultiplicativeExpr fall-through")); } // LCOV_EXCL_LINE | |
| 3701 | } | ||
| 3702 | |||
| 3703 | // Retrieve the new lhs symbol type | ||
| 3704 | 2957 | lhsSTy = opPair.getSecond(); | |
| 3705 | 2957 | opQueue.pop(); | |
| 3706 | 2957 | } | |
| 3707 | |||
| 3708 | // Return the result | ||
| 3709 | 5734 | return exprResultAny(res); | |
| 3710 | 2867 | } | |
| 3711 | |||
| 3712 | 11775 | public f<Any> IRGenerator.visitCastExpr(ASTCastExprNode* node) { | |
| 3713 | // Check if only one operand is present -> loop through | ||
| 3714 | 11775 | if !node.isCast { | |
| 3715 | ✗ | return this.visit(node.prefixUnaryExpr); | |
| 3716 | } | ||
| 3717 | |||
| 3718 | 11775 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3719 | |||
| 3720 | // It is a cast expression | ||
| 3721 | // Retrieve target symbol type | ||
| 3722 | 11775 | const QualType targetSTy = this.getEvaluatedType(cast<IVisitable*>(node)); | |
| 3723 | |||
| 3724 | // Evaluate rhs | ||
| 3725 | 11775 | IVisitable* rhsNode = node.assignExpr; | |
| 3726 | 11775 | const QualType rhsSTy = this.getEvaluatedType(rhsNode); | |
| 3727 | 11775 | LLVMExprResult rhs = this.visitExpr(rhsNode); | |
| 3728 | |||
| 3729 | // Retrieve the result value | ||
| 3730 | 11775 | const LLVMExprResult res = this.conversionManager.getCastInst(cast<ASTNode*>(node), targetSTy, rhs, rhsSTy); | |
| 3731 | |||
| 3732 | // Return the result | ||
| 3733 | 11775 | return exprResultAny(res); | |
| 3734 | } | ||
| 3735 | |||
| 3736 | 7154 | public f<Any> IRGenerator.visitPrefixUnaryExpr(ASTPrefixUnaryExprNode* node) { | |
| 3737 | // If no operator is applied, simply visit the atomic expression | ||
| 3738 | 7154 | if node.op == PrefixUnaryOp::OP_NONE { | |
| 3739 | ✗ | return this.visit(node.postfixUnaryExpr); | |
| 3740 | } | ||
| 3741 | |||
| 3742 | 7154 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3743 | |||
| 3744 | // Evaluate lhs | ||
| 3745 | 7154 | IVisitable* lhsNode = node.prefixUnaryExpr; | |
| 3746 | 7154 | const QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3747 | 7154 | LLVMExprResult lhs = this.visitExpr(lhsNode); | |
| 3748 | 7154 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 3749 | |||
| 3750 | 7154 | switch node.op { | |
| 3751 | case PrefixUnaryOp::OP_MINUS: { | ||
| 3752 | // Execute operation | ||
| 3753 | 948 | lhs = this.conversionManager.getPrefixMinusInst(baseNode, lhs, lhsSTy); | |
| 3754 | |||
| 3755 | // This operator can not work in-place, so we need additional memory | ||
| 3756 | 948 | lhs.ptr = this.insertAlloca(lhs.value.getType()); | |
| 3757 | |||
| 3758 | // Store the new value | ||
| 3759 | 948 | this.insertStore(lhs.value, lhs.ptr); | |
| 3760 | 948 | } | |
| 3761 | case PrefixUnaryOp::OP_PLUS_PLUS: { | ||
| 3762 | // Execute operation | ||
| 3763 | 16 | const LLVMExprResult res = this.conversionManager.getPrefixPlusPlusInst(baseNode, lhs, lhsSTy); | |
| 3764 | 16 | lhs.value = res.value; | |
| 3765 | |||
| 3766 | // If this operation happens on a volatile variable, store the value directly | ||
| 3767 | 16 | if lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile { | |
| 3768 | ✗ | this.insertStore(lhs.value, lhs.ptr, true); | |
| 3769 | ✗ | } | |
| 3770 | |||
| 3771 | // Save to the existing address if possible, otherwise (e.g. for literals) allocate new space | ||
| 3772 | 16 | if lhs.ptr.isNull() { | |
| 3773 | 2 | lhs.ptr = this.insertAlloca(lhs.value.getType()); | |
| 3774 | 2 | } | |
| 3775 | |||
| 3776 | // Store the new value | ||
| 3777 | 16 | this.insertStore(lhs.value, lhs.ptr); | |
| 3778 | 16 | } | |
| 3779 | case PrefixUnaryOp::OP_MINUS_MINUS: { | ||
| 3780 | // Execute operation | ||
| 3781 | 39 | const LLVMExprResult res = this.conversionManager.getPrefixMinusMinusInst(baseNode, lhs, lhsSTy); | |
| 3782 | 39 | lhs.value = res.value; | |
| 3783 | |||
| 3784 | // If this operation happens on a volatile variable, store the value directly | ||
| 3785 | 39 | if lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile { | |
| 3786 | ✗ | this.insertStore(lhs.value, lhs.ptr, true); | |
| 3787 | ✗ | } | |
| 3788 | |||
| 3789 | // Save to the existing address if possible, otherwise (e.g. for literals) allocate new space | ||
| 3790 | 39 | if lhs.ptr.isNull() { | |
| 3791 | 2 | lhs.ptr = this.insertAlloca(lhs.value.getType()); | |
| 3792 | 2 | } | |
| 3793 | |||
| 3794 | // Store the new value | ||
| 3795 | 39 | this.insertStore(lhs.value, lhs.ptr); | |
| 3796 | 39 | } | |
| 3797 | case PrefixUnaryOp::OP_NOT: { | ||
| 3798 | // Execute operation | ||
| 3799 | 3609 | lhs = this.conversionManager.getPrefixNotInst(baseNode, lhs, lhsSTy); | |
| 3800 | 3609 | } | |
| 3801 | case PrefixUnaryOp::OP_BITWISE_NOT: { | ||
| 3802 | // Execute operation | ||
| 3803 | 33 | lhs = this.conversionManager.getPrefixBitwiseNotInst(baseNode, lhs, lhsSTy); | |
| 3804 | 33 | } | |
| 3805 | case PrefixUnaryOp::OP_DEREFERENCE: { | ||
| 3806 | // If only .refPtr is filled, we can't simply rewire the fields, but need to actually perform a load. | ||
| 3807 | // For that we can use resolveValue(). | ||
| 3808 | 966 | const bool onlyRefPtrIsFilled = lhs.value.isNull() && lhs.ptr.isNull(); | |
| 3809 | // Rewire the fields | ||
| 3810 | 966 | const llvm::Value newRefPtr = onlyRefPtrIsFilled ? this.resolveValue(lhsNode, lhs) : lhs.ptr; | |
| 3811 | 966 | const llvm::Value newPtr = lhs.value; | |
| 3812 | 966 | LLVMExprResult newLhs; | |
| 3813 | 966 | newLhs.ptr = newPtr; | |
| 3814 | 966 | newLhs.refPtr = newRefPtr; | |
| 3815 | 966 | lhs = newLhs; | |
| 3816 | 966 | } | |
| 3817 | case PrefixUnaryOp::OP_ADDRESS_OF: { | ||
| 3818 | // If only .value is filled, we can't simply rewire the fields, but need to actually perform alloca + store. | ||
| 3819 | // For that we can use resolveAddress(). | ||
| 3820 | 1543 | const bool onlyValueIsFilled = lhs.ptr.isNull() && lhs.refPtr.isNull(); | |
| 3821 | // Rewire the fields | ||
| 3822 | 1543 | const llvm::Value newValue = onlyValueIsFilled ? this.resolveAddress(lhs) : lhs.ptr; | |
| 3823 | 1543 | const llvm::Value newPtr = lhs.refPtr; | |
| 3824 | 1543 | LLVMExprResult newLhs; | |
| 3825 | 1543 | newLhs.value = newValue; | |
| 3826 | 1543 | newLhs.ptr = newPtr; | |
| 3827 | 1543 | lhs = newLhs; | |
| 3828 | 1543 | } | |
| 3829 | default: { | ||
| 3830 | − | panic(Error("PrefixUnaryExpr fall-through")); // LCOV_EXCL_LINE | |
| 3831 | } | ||
| 3832 | } | ||
| 3833 | |||
| 3834 | 7154 | return exprResultAny(lhs); | |
| 3835 | } | ||
| 3836 | |||
| 3837 | 102178 | public f<Any> IRGenerator.visitPostfixUnaryExpr(ASTPostfixUnaryExprNode* node) { | |
| 3838 | // If no operator is applied, simply visit the atomic expression | ||
| 3839 | 102178 | if node.op == PostfixUnaryOp::OP_NONE { | |
| 3840 | ✗ | return this.visit(node.atomicExpr); | |
| 3841 | } | ||
| 3842 | |||
| 3843 | 102178 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 3844 | |||
| 3845 | // Evaluate lhs | ||
| 3846 | 102178 | IVisitable* lhsNode = node.postfixUnaryExpr; | |
| 3847 | 102178 | QualType lhsSTy = this.getEvaluatedType(lhsNode); | |
| 3848 | 102178 | LLVMExprResult lhs = this.visitExpr(lhsNode); | |
| 3849 | 102178 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 3850 | |||
| 3851 | 102178 | switch node.op { | |
| 3852 | case PostfixUnaryOp::OP_SUBSCRIPT: { | ||
| 3853 | 12559 | IVisitable* indexExpr = node.subscriptIndexExpr; | |
| 3854 | |||
| 3855 | // Check if we need to generate a call to an overloaded operator function | ||
| 3856 | 12559 | if this.conversionManager.callsOverloadedOpFct(baseNode, 0l) { | |
| 3857 | 924 | const Resolver lhsV = valueResolver(lhsSTy, &lhs); | |
| 3858 | 924 | const Resolver lhsP = addressResolver(&lhs); | |
| 3859 | 924 | const Resolver idxV = exprValueResolver(indexExpr); | |
| 3860 | 924 | const Resolver idxP = exprAddressResolver(indexExpr); | |
| 3861 | 924 | const OperandResolvers resolvers = OperandResolvers(lhsV, lhsP, idxV, idxP); | |
| 3862 | 924 | lhs = this.conversionManager.callOperatorOverloadFct(baseNode, resolvers, 2l, 0l); | |
| 3863 | 924 | } else { | |
| 3864 | 11635 | lhsSTy = lhsSTy.removeReferenceWrapper(); | |
| 3865 | |||
| 3866 | // Get the index value | ||
| 3867 | 11635 | const llvm::Value indexValue = this.resolveValue(indexExpr); | |
| 3868 | // Come up with the address | ||
| 3869 | 11635 | if lhsSTy.isArray() && cast<unsigned long>(lhsSTy.getArraySize()) != ARRAY_SIZE_UNKNOWN { // Array | |
| 3870 | // Make sure the address is present | ||
| 3871 | 1559 | this.resolveAddress(lhs); | |
| 3872 | |||
| 3873 | // Calculate address of array item | ||
| 3874 | 1559 | const llvm::Type lhsTy = lhsSTy.toLLVMType(this.sourceFile); | |
| 3875 | 1559 | Vector<llvm::Value> indices; | |
| 3876 | 1559 | indices.pushBack(this.getInt64(0l)); | |
| 3877 | 1559 | indices.pushBack(indexValue); | |
| 3878 | 1559 | lhs.ptr = this.insertInBoundsGEP(lhsTy, lhs.ptr, indices); | |
| 3879 | 1559 | } else { // Pointer | |
| 3880 | // Now the pointer is the value | ||
| 3881 | 10076 | lhs.ptr = this.resolveValue(lhsNode, lhs); | |
| 3882 | |||
| 3883 | 10076 | const QualType lhsContainedSTy = lhsSTy.getContained(); | |
| 3884 | 10076 | const llvm::Type lhsTy = lhsContainedSTy.toLLVMType(this.sourceFile); | |
| 3885 | // Calculate address of pointer item | ||
| 3886 | 10076 | lhs.ptr = this.insertInBoundsGEP(lhsTy, lhs.ptr, indexValue); | |
| 3887 | 10076 | } | |
| 3888 | |||
| 3889 | // Reset value and entry | ||
| 3890 | 11635 | lhs.value = llvm::Value(); | |
| 3891 | 11635 | lhs.entry = nil<const SymbolTableEntry*>; | |
| 3892 | 11635 | } | |
| 3893 | 12559 | } | |
| 3894 | case PostfixUnaryOp::OP_MEMBER_ACCESS: { | ||
| 3895 | // Get the address of the struct/union instance | ||
| 3896 | 80646 | this.resolveAddress(lhs); | |
| 3897 | 80646 | lhsSTy = lhsSTy.removeReferenceWrapper(); | |
| 3898 | |||
| 3899 | // Auto de-reference pointer | ||
| 3900 | 80646 | this.autoDeReferencePtr(lhs.ptr, lhsSTy); | |
| 3901 | |||
| 3902 | 80646 | const String& fieldName = node.identifier; | |
| 3903 | |||
| 3904 | // Union field access is handled separately, since it is backed by a tagged { i32 tag, ..., payload } layout | ||
| 3905 | // instead of one LLVM struct member per Spice field (see Type::toLLVMType's TY_UNION branch), and needs a | ||
| 3906 | // runtime tag read-guard/write-update around the payload access. | ||
| 3907 | 80646 | if lhsSTy.is(SuperType::TY_UNION) { | |
| 3908 | 65 | this.generateUnionFieldAccess(node, lhs, lhsSTy, fieldName); | |
| 3909 | 65 | } else { | |
| 3910 | 80581 | assert lhsSTy.is(SuperType::TY_STRUCT); | |
| 3911 | |||
| 3912 | // Retrieve struct | ||
| 3913 | 80581 | Struct* spiceStruct = lhsSTy.getStruct(cast<ASTNode*>(node)); | |
| 3914 | 80581 | assert spiceStruct != nil<Struct*>; | |
| 3915 | |||
| 3916 | // Retrieve field entry | ||
| 3917 | 80581 | Vector<unsigned long> indexPath; | |
| 3918 | 80581 | lhs.entry = spiceStruct.lookupInComposedFields(fieldName, indexPath); | |
| 3919 | 80581 | assert lhs.entry != nil<const SymbolTableEntry*>; | |
| 3920 | 80581 | const QualType fieldSymbolType = lhs.entry.getQualType(); | |
| 3921 | |||
| 3922 | // Get address of the field in the struct instance | ||
| 3923 | 80581 | Vector<llvm::Value> indices; | |
| 3924 | 80581 | indices.pushBack(this.getInt64(0l)); | |
| 3925 | 161182 | foreach const unsigned long index : indexPath { | |
| 3926 | 80601 | indices.pushBack(this.getInt32(cast<long>(index))); | |
| 3927 | 80601 | } | |
| 3928 | 80581 | const String name = fieldName + ".addr"; | |
| 3929 | 80581 | const llvm::Value memberAddr = this.insertInBoundsGEP(lhsSTy.toLLVMType(this.sourceFile), lhs.ptr, indices, name); | |
| 3930 | |||
| 3931 | // Set as ptr or refPtr, depending on the type | ||
| 3932 | 80581 | if fieldSymbolType.isRef() { | |
| 3933 | 905 | lhs.ptr = llvm::Value(); | |
| 3934 | 905 | lhs.refPtr = memberAddr; | |
| 3935 | 905 | } else { | |
| 3936 | 79676 | lhs.ptr = memberAddr; | |
| 3937 | 79676 | lhs.refPtr = llvm::Value(); | |
| 3938 | 79676 | } | |
| 3939 | |||
| 3940 | // Reset the value | ||
| 3941 | 80581 | lhs.value = llvm::Value(); | |
| 3942 | 80581 | } | |
| 3943 | 80646 | } | |
| 3944 | case PostfixUnaryOp::OP_PLUS_PLUS: { | ||
| 3945 | // Make sure a value is present | ||
| 3946 | 8143 | this.resolveValue(lhsNode, lhs); | |
| 3947 | |||
| 3948 | // Allocate new local variable if required | ||
| 3949 | 8143 | if lhs.ptr.isNull() { | |
| 3950 | 4 | assert !lhs.value.isNull(); | |
| 3951 | 4 | lhs.ptr = this.insertAlloca(lhs.value.getType()); | |
| 3952 | 4 | } | |
| 3953 | |||
| 3954 | // Execute operation | ||
| 3955 | 8143 | const LLVMExprResult res = this.conversionManager.getPostfixPlusPlusInst(baseNode, lhs, lhsSTy); | |
| 3956 | |||
| 3957 | // Save the new value to the old address | ||
| 3958 | 8143 | if this.conversionManager.callsOverloadedOpFct(baseNode, 0l) { | |
| 3959 | 12 | lhs.value = res.value; | |
| 3960 | 12 | lhs.ptr = res.ptr; | |
| 3961 | 12 | } else { | |
| 3962 | 8131 | this.insertStore(res.value, lhs.ptr, lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile); | |
| 3963 | 8131 | lhs.ptr = llvm::Value(); | |
| 3964 | 8131 | } | |
| 3965 | 8143 | } | |
| 3966 | case PostfixUnaryOp::OP_MINUS_MINUS: { | ||
| 3967 | // Make sure a value is present | ||
| 3968 | 737 | this.resolveValue(lhsNode, lhs); | |
| 3969 | |||
| 3970 | // Allocate new local variable if required | ||
| 3971 | 737 | if lhs.ptr.isNull() { | |
| 3972 | 4 | assert !lhs.value.isNull(); | |
| 3973 | 4 | lhs.ptr = this.insertAlloca(lhs.value.getType()); | |
| 3974 | 4 | } | |
| 3975 | |||
| 3976 | // Execute operation | ||
| 3977 | 737 | const LLVMExprResult res = this.conversionManager.getPostfixMinusMinusInst(baseNode, lhs, lhsSTy); | |
| 3978 | |||
| 3979 | // Save the new value to the old address | ||
| 3980 | 737 | if this.conversionManager.callsOverloadedOpFct(baseNode, 0l) { | |
| 3981 | 10 | lhs.value = res.value; | |
| 3982 | 10 | lhs.ptr = res.ptr; | |
| 3983 | 10 | } else { | |
| 3984 | 727 | this.insertStore(res.value, lhs.ptr, lhs.entry != nil<const SymbolTableEntry*> && lhs.entry.isVolatile); | |
| 3985 | 727 | lhs.ptr = llvm::Value(); | |
| 3986 | 727 | } | |
| 3987 | 737 | } | |
| 3988 | case PostfixUnaryOp::OP_ERR_PROPAGATION: { | ||
| 3989 | 93 | lhs = this.generateErrorPropagation(node, lhs); | |
| 3990 | 93 | } | |
| 3991 | default: { | ||
| 3992 | − | panic(Error("PostfixUnaryExpr fall-through")); // LCOV_EXCL_LINE | |
| 3993 | } | ||
| 3994 | } | ||
| 3995 | |||
| 3996 | 102178 | return exprResultAny(lhs); | |
| 3997 | } | ||
| 3998 | |||
| 3999 | // Union branch of the member access in visitPostfixUnaryExpr | ||
| 4000 | 65 | p IRGenerator.generateUnionFieldAccess(ASTPostfixUnaryExprNode* node, LLVMExprResult& lhs, const QualType& lhsSTy, const String& fieldName) { | |
| 4001 | 65 | Scope* unionScope = lhsSTy.getBodyScope(); | |
| 4002 | 65 | const SymbolTableEntry* fieldEntry = unionScope.symbolTable.lookupStrict(fieldName); | |
| 4003 | 65 | assert fieldEntry != nil<const SymbolTableEntry*>; | |
| 4004 | 65 | const QualType fieldSymbolType = fieldEntry.getQualType(); | |
| 4005 | // Tag 0 is reserved for the "unset" state, so a field's tag is its order index shifted up by one. This makes | ||
| 4006 | // an all-zero union (e.g. from .bss, a generic memset, or a nested aggregate's zero default) correctly unset | ||
| 4007 | // rather than aliasing field 0, so the tag-mismatch guard below also catches accidental zero-initialization. | ||
| 4008 | 65 | const long fieldTagIndex = cast<long>(fieldEntry.orderIndex) + 1l; | |
| 4009 | |||
| 4010 | 65 | const llvm::Type unionLLVMTy = lhsSTy.toLLVMType(this.sourceFile); | |
| 4011 | 65 | const llvm::Value unionBasePtr = lhs.ptr; | |
| 4012 | 65 | const llvm::Value tagAddr = this.insertStructGEP(unionLLVMTy, unionBasePtr, 0, String("tag.addr")); | |
| 4013 | |||
| 4014 | // A plain `u.field = value` assignment is the only access that should update the tag instead of checking it: | ||
| 4015 | // it is the sole case where this exact node is the direct, unwrapped left-hand side of a plain '=' assignment. | ||
| 4016 | // Every other use (reads, compound-assign's read-then-store-back, address-of, pass-by-ref, or descending | ||
| 4017 | // further into a by-value struct/union field) must first prove the field is currently active. | ||
| 4018 | 65 | bool isDirectAssignTarget = false; | |
| 4019 | 65 | const ASTNode* parent = node.parent; | |
| 4020 | 65 | if parent != nil<const ASTNode*> && parent.kind == ASTNodeKind::ASSIGN_EXPR { | |
| 4021 | 31 | const ASTAssignExprNode* assignParent = downcast<ASTAssignExprNode>(parent); | |
| 4022 | 31 | isDirectAssignTarget = assignParent.lhs != nil<IVisitable*> && getASTNode(assignParent.lhs) == cast<ASTNode*>(node) && assignParent.op == AssignOp::OP_ASSIGN; | |
| 4023 | 31 | } | |
| 4024 | |||
| 4025 | 65 | if isDirectAssignTarget { | |
| 4026 | 30 | this.insertStore(this.getInt32(fieldTagIndex), tagAddr); | |
| 4027 | 30 | } else { | |
| 4028 | // Guard the read with a runtime tag check. This is an always-on safety check (unlike e.g. the assert | ||
| 4029 | // statement, which is skipped in release builds), since it protects a core invariant of the union type | ||
| 4030 | // itself, not an optional user-level debug assertion. | ||
| 4031 | 35 | const llvm::Value tagValue = this.insertLoad(this.builder.getInt32Ty(), tagAddr); | |
| 4032 | 35 | const llvm::Value tagMatches = this.builder.createICmp(llvm::LLVMIntPredicate::EQ, tagValue, this.getInt32(fieldTagIndex)); | |
| 4033 | |||
| 4034 | 35 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 4035 | 35 | const llvm::BasicBlock bOk = this.createBlock("union.tag.ok." + codeLine); | |
| 4036 | 35 | const llvm::BasicBlock bPanic = this.createBlock("union.tag.panic." + codeLine); | |
| 4037 | 35 | this.insertCondJump(tagMatches, bOk, bPanic, Likelihood::LIKELY); | |
| 4038 | |||
| 4039 | 35 | this.switchToBlock(bPanic); | |
| 4040 | 35 | const llvm::Value stdErrValue = this.getStdErrValue(); | |
| 4041 | 35 | const String errorMsg = "Program panicked at " + node.codeLoc.toPrettyString() + ": active field mismatch on union field access '" + fieldName + "'\n"; | |
| 4042 | 35 | const String globalName = this.getUnusedGlobalName(String(ANON_GLOBAL_STRING_NAME)); | |
| 4043 | 35 | llvm::Value globalString = this.module.createGlobalString(errorMsg, globalName.getRaw()); | |
| 4044 | 35 | if this.cliOptions.comparableOutput { | |
| 4045 | 35 | globalString.setAlignment(4); | |
| 4046 | 35 | } | |
| 4047 | 35 | Vector<llvm::Value> fprintfArgs; | |
| 4048 | 35 | fprintfArgs.pushBack(stdErrValue); | |
| 4049 | 35 | fprintfArgs.pushBack(globalString); | |
| 4050 | 35 | this.builder.createCall(this.stdFunctionManager.getFPrintfFct(), fprintfArgs); | |
| 4051 | 35 | if this.cliOptions.printsStackTraceOnAbort() { | |
| 4052 | ✗ | Vector<llvm::Value> dumpArgs; | |
| 4053 | ✗ | dumpArgs.pushBack(this.builder.getTrue()); | |
| 4054 | ✗ | dumpArgs.pushBack(this.builder.getFalse()); | |
| 4055 | ✗ | this.builder.createCall(this.stdFunctionManager.getDumpStacktraceFct(), dumpArgs); | |
| 4056 | ✗ | } | |
| 4057 | 35 | Vector<llvm::Value> exitArgs; | |
| 4058 | 35 | exitArgs.pushBack(this.getInt32(cast<long>(EXIT_FAILURE))); | |
| 4059 | 35 | this.builder.createCall(this.stdFunctionManager.getExitFct(), exitArgs); | |
| 4060 | 35 | this.builder.createUnreachable(); | |
| 4061 | |||
| 4062 | 35 | this.switchToBlock(bOk); | |
| 4063 | 35 | } | |
| 4064 | |||
| 4065 | // Get address of the payload (index 2: { tag, alignPad, payload }) | ||
| 4066 | 65 | const llvm::Value memberAddr = this.insertStructGEP(unionLLVMTy, unionBasePtr, 2, fieldName + ".addr"); | |
| 4067 | |||
| 4068 | // Set as ptr or refPtr, depending on the type | ||
| 4069 | 65 | if fieldSymbolType.isRef() { | |
| 4070 | ✗ | lhs.ptr = llvm::Value(); | |
| 4071 | ✗ | lhs.refPtr = memberAddr; | |
| 4072 | ✗ | } else { | |
| 4073 | 65 | lhs.ptr = memberAddr; | |
| 4074 | 65 | lhs.refPtr = llvm::Value(); | |
| 4075 | 65 | } | |
| 4076 | |||
| 4077 | 65 | lhs.entry = fieldEntry; | |
| 4078 | 65 | lhs.value = llvm::Value(); | |
| 4079 | 65 | } | |
| 4080 | |||
| 4081 | // Error propagation branch of visitPostfixUnaryExpr | ||
| 4082 | 93 | f<LLVMExprResult> IRGenerator.generateErrorPropagation(ASTPostfixUnaryExprNode* node, LLVMExprResult& lhs) { | |
| 4083 | 93 | const ErrPropagationData& errPropData = node.errPropData.get(this.manIdx); | |
| 4084 | |||
| 4085 | // Get the address of the Result<T> operand | ||
| 4086 | 93 | const llvm::Value operandPtr = this.resolveAddress(lhs); | |
| 4087 | 93 | Vector<llvm::Value> operandArgs; | |
| 4088 | 93 | operandArgs.pushBack(operandPtr); | |
| 4089 | |||
| 4090 | // Call isErr() on the operand | ||
| 4091 | 93 | assert errPropData.isErrFct != nil<const model::Function*>; | |
| 4092 | 93 | const llvm::Function isErrFct = this.stdFunctionManager.getResultIsErrFct(errPropData.isErrFct); | |
| 4093 | 93 | const llvm::Value isErr = this.builder.createCall(isErrFct, operandArgs); | |
| 4094 | |||
| 4095 | // Create blocks | ||
| 4096 | 93 | const String codeLine = node.codeLoc.toPrettyLine(); | |
| 4097 | 93 | const llvm::BasicBlock bThen = this.createBlock("err.prop.then." + codeLine); | |
| 4098 | 93 | const llvm::BasicBlock bExit = this.createBlock("err.prop.exit." + codeLine); | |
| 4099 | 93 | this.insertCondJump(isErr, bThen, bExit, Likelihood::UNLIKELY); | |
| 4100 | |||
| 4101 | // Switch to then block: propagate the error as the enclosing function's Result<U> | ||
| 4102 | 93 | this.switchToBlock(bThen); | |
| 4103 | 93 | assert errPropData.getErrFct != nil<const model::Function*> && errPropData.ctorFct != nil<const model::Function*>; | |
| 4104 | 93 | const llvm::Function getErrFct = this.stdFunctionManager.getResultGetErrFct(errPropData.getErrFct); | |
| 4105 | 93 | const llvm::Value errorPtr = this.builder.createCall(getErrFct, operandArgs); | |
| 4106 | 93 | const llvm::Function errCtorFct = this.stdFunctionManager.getResultErrCtorFct(errPropData.ctorFct); | |
| 4107 | 93 | Vector<llvm::Value> errCtorArgs; | |
| 4108 | 93 | errCtorArgs.pushBack(errorPtr); | |
| 4109 | // If the enclosing function returns via memory, construct the result directly in the memory of its caller | ||
| 4110 | 93 | const QualType& propagatedResultType = errPropData.ctorFct.returnType; | |
| 4111 | 93 | const llvm::BasicBlock currentBlock = this.builder.getInsertBlock(); | |
| 4112 | 93 | const llvm::Value sretArg = getSRetArg(currentBlock.getParent()); | |
| 4113 | 93 | llvm::Value propagatedResultAddr; | |
| 4114 | 93 | llvm::Value propagatedResult = this.insertCall(errCtorFct, errCtorArgs, propagatedResultType, propagatedResultAddr, sretArg); | |
| 4115 | 93 | if !sretArg.isNull() && propagatedResultAddr.isSameAs(sretArg) { | |
| 4116 | 93 | propagatedResult = llvm::Value(); | |
| 4117 | 93 | } else if !propagatedResultAddr.isNull() { | |
| 4118 | ✗ | propagatedResult = this.insertLoad(propagatedResultType, propagatedResultAddr); | |
| 4119 | ✗ | } | |
| 4120 | |||
| 4121 | // Record this propagation hop in the error return trace, if tracing is enabled for this file | ||
| 4122 | 93 | if this.sourceFile.errorReturnTracing { | |
| 4123 | 3 | this.generateErrTraceCall(this.stdFunctionManager.getErrTracePushFct(), cast<ASTNode*>(node), "errtrace.hop.sig.", "errtrace.hop.file."); | |
| 4124 | 3 | } | |
| 4125 | |||
| 4126 | // Clean up all scopes between here and the enclosing function/procedure/lambda body, then return the error | ||
| 4127 | 93 | this.generateScopeCleanupUpTo(cast<ASTNode*>(node), this.currentScope.getFunctionScope()); | |
| 4128 | 93 | this.blockAlreadyTerminated = true; | |
| 4129 | 93 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 4130 | 93 | this.insertReturn(propagatedResult); | |
| 4131 | |||
| 4132 | // Switch to exit block: unwrap the payload, which becomes the value of the whole expression | ||
| 4133 | 93 | this.switchToBlock(bExit); | |
| 4134 | 93 | assert errPropData.unwrapFct != nil<const model::Function*>; | |
| 4135 | 93 | const llvm::Function unwrapFct = this.stdFunctionManager.getResultUnwrapFct(errPropData.unwrapFct); | |
| 4136 | 93 | const llvm::Value unwrapped = this.builder.createCall(unwrapFct, operandArgs); | |
| 4137 | 186 | return exprResultWithPtr(unwrapped); | |
| 4138 | 93 | } | |
| 4139 | |||
| 4140 | // Generate a call to an error return trace function with the signature and location of the given node | ||
| 4141 | 8 | p IRGenerator.generateErrTraceCall(llvm::Function traceFct, ASTNode* node, string sigBaseName, string fileBaseName) { | |
| 4142 | 8 | const String enclosingSignature = node.getEnclosingFunctionSignature(this.manIdx); | |
| 4143 | 8 | const llvm::Value signature = this.createGlobalStringConst(String(sigBaseName), enclosingSignature, node.codeLoc); | |
| 4144 | 8 | const llvm::Value fileName = this.createGlobalStringConst(String(fileBaseName), node.codeLoc.toPrettyFilePath(), node.codeLoc); | |
| 4145 | 8 | const llvm::Value line = this.getInt32(cast<long>(node.codeLoc.line)); | |
| 4146 | 8 | const llvm::Value column = this.getInt32(cast<long>(node.codeLoc.col)); | |
| 4147 | 8 | Vector<llvm::Value> args; | |
| 4148 | 8 | args.pushBack(signature); | |
| 4149 | 8 | args.pushBack(fileName); | |
| 4150 | 8 | args.pushBack(line); | |
| 4151 | 8 | args.pushBack(column); | |
| 4152 | 8 | this.builder.createCall(traceFct, args); | |
| 4153 | 8 | } | |
| 4154 | |||
| 4155 | 397941 | public f<Any> IRGenerator.visitAtomicExpr(ASTAtomicExprNode* node) { | |
| 4156 | // If constant | ||
| 4157 | 397941 | if node.constant != nil<ASTConstantNode*> { | |
| 4158 | 73709 | const llvm::Value constantValue = this.visitConst(cast<IVisitable*>(node.constant)); | |
| 4159 | 73709 | return exprResultAny(exprResultWithConstant(constantValue)); | |
| 4160 | } | ||
| 4161 | |||
| 4162 | // If value | ||
| 4163 | 324232 | if node.value != nil<IVisitable*> { | |
| 4164 | 88607 | return this.visit(node.value); | |
| 4165 | } | ||
| 4166 | |||
| 4167 | // Is assign expression | ||
| 4168 | 235625 | if node.assignExpr != nil<IVisitable*> { | |
| 4169 | 1937 | return this.visit(node.assignExpr); | |
| 4170 | } | ||
| 4171 | |||
| 4172 | 233688 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 4173 | |||
| 4174 | // Identifier (local or global variable access) | ||
| 4175 | 233688 | assert !node.identifierFragments.isEmpty(); | |
| 4176 | |||
| 4177 | // Get symbol table entry | ||
| 4178 | 233688 | const VarAccessData& accessData = node.data.get(this.manIdx); | |
| 4179 | 233688 | const SymbolTableEntry* entry = accessData.entry; | |
| 4180 | 233688 | Scope* accessScope = accessData.accessScope; | |
| 4181 | 233688 | const Capture* capture = accessData.capture; | |
| 4182 | 233688 | assert entry != nil<const SymbolTableEntry*>; | |
| 4183 | 233688 | assert accessScope != nil<Scope*>; | |
| 4184 | 233688 | const QualType varSymbolType = entry.getQualType(); | |
| 4185 | 233688 | const llvm::Type varType = varSymbolType.toLLVMType(this.sourceFile); | |
| 4186 | |||
| 4187 | // If this is a function/procedure reference, return it as value. The fat pointer must point at a thunk that | ||
| 4188 | // carries the (ignored) trailing capture-struct pointer, so it can be called through the uniform lambda ABI. | ||
| 4189 | 233688 | if entry.global && (varSymbolType.is(SuperType::TY_FUNCTION) || varSymbolType.is(SuperType::TY_PROCEDURE)) { | |
| 4190 | // The referenced function might be defined later, in another source file or externally, so declare it if required. | ||
| 4191 | // The type checker only allows to reference functions with a single manifestation. | ||
| 4192 | 64 | Vector<model::Function*>* manifestations = entry.declNode.getFctManifestations(entry.name); | |
| 4193 | 64 | assert manifestations.getSize() == 1l; | |
| 4194 | 64 | const llvm::Function target = this.getOrDeclareFunction(manifestations.front()); | |
| 4195 | 64 | const llvm::Function thunk = this.getOrCreateFatFctPtrThunk(target); | |
| 4196 | 64 | const llvm::Value fatPtr = this.buildFatFctPtr(nil<Scope*>, llvm::Type(), thunk.asValue()); | |
| 4197 | 64 | LLVMExprResult res; | |
| 4198 | 64 | res.ptr = fatPtr; | |
| 4199 | 64 | res.entry = entry; | |
| 4200 | 64 | return exprResultAny(res); | |
| 4201 | } | ||
| 4202 | |||
| 4203 | // Check if external global variable | ||
| 4204 | 233624 | if entry.global && accessScope.isImportedBy(this.rootScope) { | |
| 4205 | // External global variables need to be declared and allocated in the current module | ||
| 4206 | 329 | const llvm::Value varAddress = this.module.getOrInsertGlobal(entry.name.getRaw(), varType); | |
| 4207 | 329 | this.updateAddress(entry, varAddress); | |
| 4208 | 329 | } | |
| 4209 | |||
| 4210 | // Check if enum item | ||
| 4211 | 233624 | if accessScope.scopeType == ScopeType::ENUM { | |
| 4212 | 2282 | const ASTEnumItemNode* itemNode = downcast<ASTEnumItemNode>(entry.declNode); | |
| 4213 | 2282 | const llvm::Value constantItemValue = llvm::getConstInt(varType, cast<unsigned long>(itemNode.itemValue)); | |
| 4214 | 2282 | LLVMExprResult res; | |
| 4215 | 2282 | res.constant = constantItemValue; | |
| 4216 | 2282 | res.entry = entry; | |
| 4217 | 2282 | return exprResultAny(res); | |
| 4218 | } | ||
| 4219 | |||
| 4220 | 231342 | const llvm::Value address = this.getAddress(entry); | |
| 4221 | 231342 | assert !address.isNull(); | |
| 4222 | |||
| 4223 | // Load the address of the referenced variable | ||
| 4224 | 231342 | LLVMExprResult res; | |
| 4225 | 231342 | res.entry = entry; | |
| 4226 | 231342 | if varSymbolType.isRef() || (capture != nil<const Capture*> && capture.getMode() == CapturePassMode::BY_REFERENCE) { | |
| 4227 | 22777 | res.refPtr = address; | |
| 4228 | 22777 | return exprResultAny(res); | |
| 4229 | } | ||
| 4230 | |||
| 4231 | 208565 | res.ptr = address; | |
| 4232 | 208565 | return exprResultAny(res); | |
| 4233 | } | ||
| 4234 | |||
| 4235 | // =========================================================== Values ============================================================ | ||
| 4236 | |||
| 4237 | 88607 | public f<Any> IRGenerator.visitValue(ASTValueNode* node) { | |
| 4238 | 88607 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 4239 | |||
| 4240 | // Function call | ||
| 4241 | 88607 | if node.fctCall != nil<IVisitable*> { | |
| 4242 | 76601 | return this.visit(node.fctCall); | |
| 4243 | } | ||
| 4244 | |||
| 4245 | // Array initialization | ||
| 4246 | 12006 | if node.arrayInitialization != nil<IVisitable*> { | |
| 4247 | 160 | return this.visit(node.arrayInitialization); | |
| 4248 | } | ||
| 4249 | |||
| 4250 | // Struct instantiation | ||
| 4251 | 11846 | if node.structInstantiation != nil<IVisitable*> { | |
| 4252 | 1662 | return this.visit(node.structInstantiation); | |
| 4253 | } | ||
| 4254 | |||
| 4255 | // Lambda function | ||
| 4256 | 10184 | if node.lambdaFunc != nil<IVisitable*> { | |
| 4257 | 61 | return this.visit(node.lambdaFunc); | |
| 4258 | } | ||
| 4259 | |||
| 4260 | // Lambda procedure | ||
| 4261 | 10123 | if node.lambdaProc != nil<IVisitable*> { | |
| 4262 | 71 | return this.visit(node.lambdaProc); | |
| 4263 | } | ||
| 4264 | |||
| 4265 | // Lambda expression | ||
| 4266 | 10052 | if node.lambdaExpr != nil<IVisitable*> { | |
| 4267 | 4 | return this.visit(node.lambdaExpr); | |
| 4268 | } | ||
| 4269 | |||
| 4270 | 10048 | if node.isNil { | |
| 4271 | // Retrieve type of the nil constant | ||
| 4272 | 10048 | const llvm::Type nilType = this.visitType(cast<IVisitable*>(node.nilType)); | |
| 4273 | // Create constant nil value | ||
| 4274 | 10048 | const llvm::Value nilValue = llvm::getNullValue(nilType); | |
| 4275 | // Return it | ||
| 4276 | 10048 | return exprResultAny(exprResultWithConstant(nilValue)); | |
| 4277 | } | ||
| 4278 | |||
| 4279 | − | panic(Error("Value fall-through")); // LCOV_EXCL_LINE | |
| 4280 | } | ||
| 4281 | |||
| 4282 | 77979 | public f<Any> IRGenerator.visitConstant(ASTConstantNode* node) { | |
| 4283 | 77979 | if this.currentScope != this.rootScope { | |
| 4284 | 73802 | this.diGenerator.setSourceLocation(cast<ASTNode*>(node)); | |
| 4285 | 73802 | } | |
| 4286 | 77979 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 4287 | 77979 | const CompileTimeValue compileTimeValue = baseNode.getCompileTimeValue(this.manIdx); | |
| 4288 | 77979 | const QualType evaluatedType = this.getEvaluatedType(cast<IVisitable*>(node)); | |
| 4289 | 77979 | return Any(this.getConst(compileTimeValue, evaluatedType, baseNode)); | |
| 4290 | } | ||
| 4291 | |||
| 4292 | // Check if an argument of the given type matches the expected parameter type | ||
| 4293 | 92982 | f<bool> matchArgType(const QualType& origExpectedTy, const QualType& origActualTy) { | |
| 4294 | 92982 | if origExpectedTy.matches(origActualTy, false, true, true) { | |
| 4295 | 85537 | return true; | |
| 4296 | } | ||
| 4297 | |||
| 4298 | // Unwrap as far as possible and remove reference wrappers if possible | ||
| 4299 | 7445 | QualType expectedTy = origExpectedTy; | |
| 4300 | 7445 | QualType actualTy = origActualTy; | |
| 4301 | 7445 | unwrapBoth(expectedTy, actualTy); | |
| 4302 | 7445 | return expectedTy.matchesInterfaceImplementedByStruct(actualTy); | |
| 4303 | } | ||
| 4304 | |||
| 4305 | 76601 | public f<Any> IRGenerator.visitFctCall(ASTFctCallNode* node) { | |
| 4306 | // Check if this is a builtin call | ||
| 4307 | 76601 | if isBuiltinFunction(node.fqFunctionName) { | |
| 4308 | 9122 | return this.visitBuiltinCall(node); | |
| 4309 | } | ||
| 4310 | |||
| 4311 | 67479 | const FctCallData& data = node.data.get(this.manIdx); | |
| 4312 | |||
| 4313 | 67479 | const model::Function* spiceFunc = data.callee; | |
| 4314 | 67479 | assert data.isFctPtrCall() || spiceFunc != nil<const model::Function*>; // If not a function pointer call, we must have a function | |
| 4315 | 67479 | String mangledName; | |
| 4316 | 67479 | if !data.isFctPtrCall() { | |
| 4317 | 67314 | mangledName = spiceFunc.getMangledName(); | |
| 4318 | 67314 | } | |
| 4319 | |||
| 4320 | // Get entry of the first fragment | ||
| 4321 | 67479 | const SymbolTableEntry* firstFragEntry = this.currentScope.lookup(node.functionNameFragments.front()); | |
| 4322 | |||
| 4323 | // Get this type | ||
| 4324 | 67479 | Vector<llvm::Value> argValues; | |
| 4325 | 67479 | llvm::Value thisPtr; | |
| 4326 | 67479 | if data.isMethodCall() { | |
| 4327 | 27641 | assert !data.isCtorCall(); | |
| 4328 | |||
| 4329 | // Retrieve entry of the first fragment | ||
| 4330 | 27641 | assert firstFragEntry != nil<const SymbolTableEntry*>; | |
| 4331 | 27641 | const QualType& firstFragQualType = firstFragEntry.getQualType(); | |
| 4332 | 27641 | const QualType baseType = firstFragQualType.getBase(); | |
| 4333 | 27641 | assert baseType.is(SuperType::TY_STRUCT) || baseType.is(SuperType::TY_INTERFACE); | |
| 4334 | 27641 | QualType structType = baseType; | |
| 4335 | |||
| 4336 | // Get address of the referenced variable / struct instance | ||
| 4337 | 27641 | thisPtr = this.getAddress(firstFragEntry); | |
| 4338 | 27641 | assert !thisPtr.isNull(); | |
| 4339 | |||
| 4340 | // Auto de-reference 'this' pointer | ||
| 4341 | 27641 | QualType firstFragmentType = firstFragEntry.getQualType(); | |
| 4342 | 27641 | this.autoDeReferencePtr(thisPtr, firstFragmentType); | |
| 4343 | 27641 | llvm::Type structTy = baseType.toLLVMType(this.sourceFile); | |
| 4344 | |||
| 4345 | // Traverse through structs - the first fragment is already looked up and the last one is the function name | ||
| 4346 | 31352 | for unsigned long i = 1l; i < node.functionNameFragments.getSize() - 1l; i++ { | |
| 4347 | 3711 | const String& identifier = node.functionNameFragments.get(i); | |
| 4348 | // Retrieve field entry, also looking through composed fields | ||
| 4349 | 3711 | Struct* spiceStruct = structType.getStruct(cast<ASTNode*>(node)); | |
| 4350 | 3711 | assert spiceStruct != nil<Struct*>; | |
| 4351 | 3711 | Vector<unsigned long> indexPath; | |
| 4352 | 3711 | const SymbolTableEntry* fieldEntry = spiceStruct.lookupInComposedFields(identifier, indexPath); | |
| 4353 | 3711 | assert fieldEntry != nil<const SymbolTableEntry*>; | |
| 4354 | 3711 | QualType fieldEntryType = fieldEntry.getQualType(); | |
| 4355 | 3711 | const QualType fieldBaseType = fieldEntryType.getBase(); | |
| 4356 | 3711 | assert fieldBaseType.is(SuperType::TY_STRUCT) || fieldBaseType.is(SuperType::TY_INTERFACE); | |
| 4357 | // Get struct type | ||
| 4358 | 3711 | structType = fieldBaseType; | |
| 4359 | // Get address of field. If the field was found directly (no composition involved), keep using the | ||
| 4360 | // single-index helper, since it elides the GEP entirely for fields at offset 0. Otherwise, step through | ||
| 4361 | // all composed fields on the way in a single GEP. | ||
| 4362 | 3711 | if indexPath.getSize() == 1l { | |
| 4363 | 3710 | thisPtr = this.insertStructGEP(structTy, thisPtr, cast<unsigned int>(indexPath.front())); | |
| 4364 | 3710 | } else { | |
| 4365 | 1 | Vector<llvm::Value> indices; | |
| 4366 | 1 | indices.pushBack(this.getInt64(0l)); | |
| 4367 | 3 | foreach const unsigned long index : indexPath { | |
| 4368 | 2 | indices.pushBack(this.getInt32(cast<long>(index))); | |
| 4369 | 2 | } | |
| 4370 | 1 | thisPtr = this.insertInBoundsGEP(structTy, thisPtr, indices); | |
| 4371 | 1 | } | |
| 4372 | // Auto de-reference pointer and get new struct type | ||
| 4373 | 3711 | this.autoDeReferencePtr(thisPtr, fieldEntryType); | |
| 4374 | 3711 | const QualType newBaseType = fieldEntryType.getBase(); | |
| 4375 | 3711 | structTy = newBaseType.toLLVMType(this.sourceFile); | |
| 4376 | 3711 | } | |
| 4377 | |||
| 4378 | // Methods, that are inherited from an interface default method, expect a pointer to the interface part of the struct | ||
| 4379 | 27641 | if spiceFunc != nil<const model::Function*> && spiceFunc.isInheritedDefaultMethod() { | |
| 4380 | 9 | thisPtr = this.getUpcastedStructPtr(thisPtr, spiceFunc.defaultMethod.thisType, structType); | |
| 4381 | 9 | } | |
| 4382 | |||
| 4383 | // Add 'this' pointer to the front of the argument list | ||
| 4384 | 27641 | argValues.pushBack(thisPtr); | |
| 4385 | 27641 | } | |
| 4386 | |||
| 4387 | 67479 | if data.isCtorCall() { | |
| 4388 | 8916 | assert !data.isMethodCall(); | |
| 4389 | |||
| 4390 | 8916 | const llvm::Type thisType = spiceFunc.thisType.toLLVMType(this.sourceFile); | |
| 4391 | 8916 | thisPtr = this.insertAlloca(thisType); | |
| 4392 | |||
| 4393 | // Add 'this' pointer to the front of the argument list | ||
| 4394 | 8916 | argValues.pushBack(thisPtr); | |
| 4395 | 8916 | } | |
| 4396 | |||
| 4397 | // Every callable behind a fat function pointer uses the same calling convention: the capture-struct pointer | ||
| 4398 | // (fat ptr slot 1) is always passed as the trailing argument, after all real arguments, regardless of whether the | ||
| 4399 | // target actually captures. Non-capturing lambdas and plain function references ignore it. Passing captures last | ||
| 4400 | // (instead of first) keeps fat ptr slot 0 directly callable through a plain C calling convention, so a Spice | ||
| 4401 | // function or lambda cast to a raw pointer can be handed to a C API as a callback without shifting its arguments. | ||
| 4402 | 67479 | llvm::Value fctPtr; | |
| 4403 | |||
| 4404 | // Get arg values | ||
| 4405 | 67479 | if node.hasArgs { | |
| 4406 | 50977 | const Vector<IVisitable*>& args = node.argLst.args; | |
| 4407 | 50977 | argValues.reserve(args.getSize()); | |
| 4408 | 50977 | QualTypeList paramSTypes; | |
| 4409 | 50977 | if data.isFctPtrCall() { | |
| 4410 | 113 | const QualType& firstFragQualType = firstFragEntry.getQualType(); | |
| 4411 | 113 | const QualType firstFragBaseType = firstFragQualType.getBase(); | |
| 4412 | 113 | paramSTypes = firstFragBaseType.getFunctionParamTypes(); | |
| 4413 | 113 | } else { | |
| 4414 | 50864 | paramSTypes = spiceFunc.getParamTypes(); | |
| 4415 | 50864 | } | |
| 4416 | 50977 | assert paramSTypes.getSize() == args.getSize(); | |
| 4417 | 50977 | const Vector<ArgInfo>& argInfos = node.argLst.argInfos.get(this.manIdx); | |
| 4418 | 136644 | for unsigned long i = 0l; i < args.getSize(); i++ { | |
| 4419 | 85667 | IVisitable* argNode = args.get(i); | |
| 4420 | 85667 | const ArgInfo& argInfo = argInfos.get(i); | |
| 4421 | 85667 | const model::Function* copyCtor = argInfo.copyCtor; | |
| 4422 | |||
| 4423 | 85667 | const QualType& expectedSTy = paramSTypes.get(i); | |
| 4424 | 85667 | const QualType actualSTy = this.getEvaluatedType(argNode); | |
| 4425 | |||
| 4426 | 85667 | if copyCtor != nil<const model::Function*> { | |
| 4427 | // By-value params of non-trivially copyable types get a copy of the argument, also if it is a reference | ||
| 4428 | 43 | const QualType actualValueSTy = actualSTy.removeReferenceWrapper(); | |
| 4429 | 43 | assert !actualValueSTy.isTriviallyCopyable(cast<ASTNode*>(node)); | |
| 4430 | 43 | const llvm::Value originalPtr = this.resolveAddress(argNode); | |
| 4431 | 43 | const llvm::Value valueCopyPtr = this.generateArgCopy(originalPtr, actualValueSTy, copyCtor); | |
| 4432 | |||
| 4433 | // Only external functions leave the copy to the caller. Attach the address of the copy to its anonymous symbol | ||
| 4434 | 43 | const SymbolTableEntry* anonymousSymbol = this.currentScope.symbolTable.lookupAnonymous(getASTNode(argNode), ULONG_MAX_VALUE); | |
| 4435 | 43 | if anonymousSymbol != nil<const SymbolTableEntry*> { | |
| 4436 | ✗ | this.updateAddress(anonymousSymbol, valueCopyPtr); | |
| 4437 | ✗ | } | |
| 4438 | |||
| 4439 | 43 | argValues.pushBack(this.insertLoad(actualValueSTy.toLLVMType(this.sourceFile), valueCopyPtr)); | |
| 4440 | 85667 | } else if matchArgType(expectedSTy, actualSTy) { | |
| 4441 | // Resolve address if actual type is reference or a decaying array, otherwise value | ||
| 4442 | 78191 | if actualSTy.isRef() || expectedSTy.isDecayedArray() { | |
| 4443 | 3378 | argValues.pushBack(this.resolveAddress(argNode)); | |
| 4444 | 3378 | } else { | |
| 4445 | 74813 | argValues.pushBack(this.resolveValue(argNode)); | |
| 4446 | 74813 | } | |
| 4447 | 85624 | } else if expectedSTy.isRef() && matchArgType(expectedSTy.getContained(), actualSTy) { // Matches with ref | |
| 4448 | 6735 | const llvm::Value argAddress = this.resolveAddress(argNode); | |
| 4449 | 6735 | argValues.pushBack(argAddress); | |
| 4450 | 7433 | } else if actualSTy.isRef() && matchArgType(expectedSTy, actualSTy.getContained()) { // Matches with ref | |
| 4451 | 623 | const llvm::Value argAddress = this.resolveValue(argNode); | |
| 4452 | 623 | argValues.pushBack(argAddress); | |
| 4453 | 623 | } else { // Need implicit cast | |
| 4454 | 75 | const llvm::Value argAddress = this.resolveAddress(argNode); | |
| 4455 | 75 | argValues.pushBack(this.doImplicitCast(argAddress, expectedSTy, actualSTy)); | |
| 4456 | 85624 | } | |
| 4457 | |||
| 4458 | // Decayed array params expect the address of a writable array, which not all of the paths above produce | ||
| 4459 | 85667 | if expectedSTy.isDecayedArray() { | |
| 4460 | 24 | llvm::Value& argValue = argValues.back(); | |
| 4461 | 24 | argValue = this.materializeDecayedArrayArg(argValue, expectedSTy); | |
| 4462 | 24 | } | |
| 4463 | 85667 | } | |
| 4464 | 50977 | } | |
| 4465 | |||
| 4466 | // Append the trailing capture-struct pointer for fat function pointer calls (see the ABI note above) | ||
| 4467 | 67479 | if data.isFctPtrCall() { | |
| 4468 | 165 | llvm::Value fatPtr = this.getAddress(firstFragEntry); | |
| 4469 | // The fat pointer might be reached via pointers or references (e.g. a param of type f<int>()** or f<int>()&) | ||
| 4470 | 165 | QualType fatPtrType = firstFragEntry.getQualType(); | |
| 4471 | 165 | this.autoDeReferencePtr(fatPtr, fatPtrType); | |
| 4472 | // Remember the address of the function pointer slot for the call below | ||
| 4473 | 165 | fctPtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatPtr, 0); | |
| 4474 | // Load the captures pointer and add it to the argument list as the trailing argument | ||
| 4475 | 165 | const llvm::Value capturesPtrPtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatPtr, 1); | |
| 4476 | 165 | const llvm::Value capturesPtr = this.insertLoad(this.builder.getPtrTy(), capturesPtrPtr, false, String(CAPTURES_PARAM_NAME)); | |
| 4477 | 165 | argValues.pushBack(capturesPtr); | |
| 4478 | 165 | } | |
| 4479 | |||
| 4480 | // Retrieve return and param types | ||
| 4481 | 67479 | QualType returnSType = QualType(SuperType::TY_DYN); | |
| 4482 | 67479 | QualTypeList paramSTypes; | |
| 4483 | 67479 | if data.isFctPtrCall() { | |
| 4484 | 165 | const QualType& firstFragQualType = firstFragEntry.getQualType(); | |
| 4485 | 165 | const QualType firstFragBaseType = firstFragQualType.getBase(); | |
| 4486 | 165 | if firstFragQualType.isBase(SuperType::TY_FUNCTION) { | |
| 4487 | 69 | returnSType = firstFragBaseType.getFunctionReturnType(); | |
| 4488 | 69 | } | |
| 4489 | 165 | paramSTypes = firstFragBaseType.getFunctionParamTypes(); | |
| 4490 | 165 | } else { | |
| 4491 | 67314 | returnSType = spiceFunc.returnType; | |
| 4492 | 67314 | paramSTypes = spiceFunc.getParamTypes(); | |
| 4493 | 67314 | } | |
| 4494 | |||
| 4495 | // Function is not defined in the current module -> declare it | ||
| 4496 | 67479 | llvm::Type fctType; | |
| 4497 | 67479 | const llvm::Function existingFct = this.module.getFunction(mangledName.getRaw()); | |
| 4498 | 67479 | if !data.isFctPtrCall() && !existingFct.isNull() { | |
| 4499 | 53786 | fctType = existingFct.getFunctionType(); | |
| 4500 | 53786 | } else { | |
| 4501 | // Get arg types | ||
| 4502 | 13693 | Vector<llvm::Type> argTypes; | |
| 4503 | 13693 | if data.isMethodCall() || data.isCtorCall() { | |
| 4504 | 9015 | argTypes.pushBack(this.builder.getPtrTy()); // This pointer | |
| 4505 | 9015 | } | |
| 4506 | 25965 | foreach const QualType& paramType : paramSTypes { | |
| 4507 | 12272 | argTypes.pushBack(paramType.getParamLLVMType(this.sourceFile)); | |
| 4508 | 12272 | } | |
| 4509 | 13693 | if data.isFctPtrCall() { | |
| 4510 | 165 | argTypes.pushBack(this.builder.getPtrTy()); // Trailing capture pointer (always present in the uniform lambda ABI) | |
| 4511 | 165 | } | |
| 4512 | |||
| 4513 | 13693 | fctType = this.getFunctionType(returnSType, argTypes); | |
| 4514 | 13693 | if !data.isFctPtrCall() && !data.isVirtualMethodCall() { | |
| 4515 | 13469 | this.module.getOrInsertFunction(mangledName.getRaw(), fctType); | |
| 4516 | 13469 | const ReturnABIInfo returnABI = this.getReturnABIInfo(returnSType); | |
| 4517 | 13469 | if returnABI.isIndirect() { | |
| 4518 | 1780 | this.addSRetParamAttrs(this.module.getFunction(mangledName.getRaw()), returnABI.memoryType); | |
| 4519 | 1780 | } | |
| 4520 | 13469 | } | |
| 4521 | 13693 | } | |
| 4522 | 67479 | assert !fctType.isNull(); | |
| 4523 | |||
| 4524 | // Retrieve the callee | ||
| 4525 | 67479 | llvm::Value callee; | |
| 4526 | 67479 | if data.isVirtualMethodCall() { | |
| 4527 | 64 | assert data.callee.isVirtual; | |
| 4528 | 64 | assert !thisPtr.isNull(); | |
| 4529 | // Load VTable | ||
| 4530 | 64 | const llvm::Value vtablePtr = this.insertLoad(this.builder.getPtrTy(), thisPtr, false, String("vtable.addr")); | |
| 4531 | 64 | const unsigned long vtableIndex = data.callee.vtableIndex; | |
| 4532 | // Lookup function pointer in VTable | ||
| 4533 | 64 | fctPtr = this.insertInBoundsGEP(this.builder.getPtrTy(), vtablePtr, this.builder.getInt64(vtableIndex), String("vfct.addr")); | |
| 4534 | 64 | callee = this.insertLoad(this.builder.getPtrTy(), fctPtr, false, String("fct")); | |
| 4535 | 67479 | } else if data.isFctPtrCall() { | |
| 4536 | 165 | assert !fctPtr.isNull(); // Already dereferenced above, where the captures pointer is loaded | |
| 4537 | 165 | callee = this.insertLoad(this.builder.getPtrTy(), fctPtr, false, String("fct")); | |
| 4538 | 165 | } else { | |
| 4539 | // Get callee function | ||
| 4540 | 67250 | const llvm::Function calleeFct = this.module.getFunction(mangledName.getRaw()); | |
| 4541 | 67250 | assert !calleeFct.isNull(); | |
| 4542 | 67250 | callee = calleeFct.asValue(); | |
| 4543 | 67415 | } | |
| 4544 | |||
| 4545 | // Generate function call. If the value is returned via memory or coerced, the call result lives in memory | ||
| 4546 | 67479 | llvm::Value resultAddr; | |
| 4547 | 67479 | const llvm::Value callInst = this.insertCall(fctType, callee, argValues, returnSType, resultAddr); | |
| 4548 | |||
| 4549 | // Set argument and return value attributes | ||
| 4550 | 67479 | if !data.isFctPtrCall() { | |
| 4551 | 67314 | this.setCallArgAttrs(callInst, spiceFunc, paramSTypes, this.getArgOffset(returnSType)); | |
| 4552 | 67314 | this.setCallReturnValAttrs(callInst, returnSType); | |
| 4553 | 67314 | } | |
| 4554 | |||
| 4555 | // Start a fresh error return trace at this call site, since it is where a new error was just put into existence | ||
| 4556 | 67479 | if node.isErrorTraceOrigin { | |
| 4557 | 5 | this.generateErrTraceCall(this.stdFunctionManager.getErrTraceResetFct(), cast<ASTNode*>(node), "errtrace.origin.sig.", "errtrace.origin.file."); | |
| 4558 | 5 | } | |
| 4559 | |||
| 4560 | // Attach address to anonymous symbol to keep track of de-allocation | ||
| 4561 | 67479 | const SymbolTableEntry* anonymousSymbol = nil<const SymbolTableEntry*>; | |
| 4562 | 67479 | llvm::Value resultPtr; | |
| 4563 | 67479 | if returnSType.is(SuperType::TY_STRUCT) || data.isCtorCall() { | |
| 4564 | 13721 | anonymousSymbol = this.currentScope.symbolTable.lookupAnonymous(cast<ASTNode*>(node)); | |
| 4565 | 13721 | if anonymousSymbol != nil<const SymbolTableEntry*> { | |
| 4566 | 2094 | if data.isCtorCall() { | |
| 4567 | 1427 | this.updateAddress(anonymousSymbol, thisPtr); | |
| 4568 | 1427 | } else { | |
| 4569 | 667 | resultPtr = resultAddr; | |
| 4570 | 667 | if resultPtr.isNull() { | |
| 4571 | ✗ | resultPtr = this.insertAlloca(callInst.getType()); | |
| 4572 | ✗ | this.insertStore(callInst, resultPtr); | |
| 4573 | ✗ | } | |
| 4574 | 667 | this.updateAddress(anonymousSymbol, resultPtr); | |
| 4575 | 667 | } | |
| 4576 | 2094 | } | |
| 4577 | 13721 | } | |
| 4578 | |||
| 4579 | 67479 | LLVMExprResult res; | |
| 4580 | 67479 | res.entry = anonymousSymbol; | |
| 4581 | |||
| 4582 | // In case this is a constructor call, return the thisPtr as pointer | ||
| 4583 | 67479 | if data.isCtorCall() { | |
| 4584 | 8916 | res.ptr = thisPtr; | |
| 4585 | 8916 | res.refPtr = resultPtr; | |
| 4586 | 17832 | return exprResultAny(res); | |
| 4587 | } | ||
| 4588 | |||
| 4589 | // In case this is a callee, returning a reference, return the address | ||
| 4590 | 58563 | if returnSType.isRef() { | |
| 4591 | 2793 | res.ptr = callInst; | |
| 4592 | 2793 | res.refPtr = resultPtr; | |
| 4593 | 5586 | return exprResultAny(res); | |
| 4594 | } | ||
| 4595 | |||
| 4596 | // In case the value is returned via memory or coerced, return its address | ||
| 4597 | 55770 | if !resultAddr.isNull() { | |
| 4598 | 4878 | res.ptr = resultAddr; | |
| 4599 | 9756 | return exprResultAny(res); | |
| 4600 | } | ||
| 4601 | |||
| 4602 | // Otherwise return the value | ||
| 4603 | 50892 | res.value = callInst; | |
| 4604 | 50892 | res.ptr = resultPtr; | |
| 4605 | 101784 | return exprResultAny(res); | |
| 4606 | 67479 | } | |
| 4607 | |||
| 4608 | 67314 | p IRGenerator.setCallArgAttrs(llvm::Value callInst, const model::Function* spiceFunc, const QualTypeList& paramSTypes, | |
| 4609 | 67314 | unsigned int argOffset = 0) { | |
| 4610 | 67314 | const bool isFctPtr = spiceFunc == nil<const model::Function*>; | |
| 4611 | 67314 | const bool isMethod = !isFctPtr && !spiceFunc.thisType.is(SuperType::TY_DYN); | |
| 4612 | 67314 | const unsigned long expectedParamCount = isMethod || isFctPtr ? paramSTypes.getSize() + 1l : paramSTypes.getSize(); | |
| 4613 | 189407 | for unsigned long i = 0l; i < expectedParamCount; i++ { | |
| 4614 | 122093 | QualType paramType = QualType(SuperType::TY_INVALID); | |
| 4615 | 122093 | if i == 0l && isMethod { | |
| 4616 | 36557 | paramType = spiceFunc.thisType.toPtr(nil<ASTNode*>); | |
| 4617 | 36557 | } else { | |
| 4618 | 85536 | paramType = paramSTypes.get(isMethod ? i - 1l : i); | |
| 4619 | 85536 | } | |
| 4620 | 122093 | const unsigned int argNo = argOffset + cast<unsigned int>(i); | |
| 4621 | |||
| 4622 | // NoUndef attribute | ||
| 4623 | 122093 | callInst.addCallParamAttr(argNo, "noundef"); | |
| 4624 | |||
| 4625 | 122093 | if paramType.isPtr() { | |
| 4626 | 52482 | const QualType pointeeSType = paramType.getContained(); | |
| 4627 | 52482 | const llvm::Type pointeeType = pointeeSType.toLLVMType(this.sourceFile); | |
| 4628 | 52482 | assert !pointeeType.isNull(); | |
| 4629 | // NonNull and dereferenceable attributes. Only 'this' is guaranteed to point to a valid object, other pointers may be nil | ||
| 4630 | 52482 | if i == 0l && isMethod { | |
| 4631 | 36557 | callInst.addCallParamAttr(argNo, "nonnull"); | |
| 4632 | 36557 | callInst.addCallDereferenceableParamAttr(argNo, this.module.getTypeStoreSize(pointeeType)); | |
| 4633 | 36557 | } | |
| 4634 | // Alignment attribute | ||
| 4635 | 52482 | callInst.addCallParamAttr(argNo, "align", cast<unsigned long>(this.module.getABITypeAlign(pointeeType))); | |
| 4636 | 52482 | } | |
| 4637 | |||
| 4638 | // ZExt or SExt attribute | ||
| 4639 | 122093 | const string extAttrKind = this.getExtAttrKindForType(paramType); | |
| 4640 | 122093 | if extAttrKind != nil<string> { | |
| 4641 | 12701 | callInst.addCallParamAttr(argNo, extAttrKind); | |
| 4642 | 12701 | } | |
| 4643 | 122093 | } | |
| 4644 | 67314 | } | |
| 4645 | |||
| 4646 | 67314 | p IRGenerator.setCallReturnValAttrs(llvm::Value callInst, const QualType& returnType) { | |
| 4647 | 67314 | if returnType.is(SuperType::TY_DYN) { | |
| 4648 | 25222 | return; | |
| 4649 | } | ||
| 4650 | |||
| 4651 | // Values, that are returned via memory, have attributes on the sret argument. Coerced values might contain padding | ||
| 4652 | 42092 | const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType); | |
| 4653 | 42092 | if !returnABI.isDirect() { | |
| 4654 | 4869 | return; | |
| 4655 | } | ||
| 4656 | |||
| 4657 | // NoUndef attribute | ||
| 4658 | 37223 | callInst.addCallRetAttr("noundef"); | |
| 4659 | // ZExt or SExt attribute | ||
| 4660 | 37223 | const string extAttrKind = this.getExtAttrKindForType(returnType); | |
| 4661 | 37223 | if extAttrKind != nil<string> { | |
| 4662 | 11385 | callInst.addCallRetAttr(extAttrKind); | |
| 4663 | 11385 | } | |
| 4664 | 37223 | } | |
| 4665 | |||
| 4666 | 160 | public f<Any> IRGenerator.visitArrayInitialization(ASTArrayInitializationNode* node) { | |
| 4667 | // Return immediately if the initialization is empty | ||
| 4668 | 160 | if node.actualSize == 0l { | |
| 4669 | ✗ | LLVMExprResult res; | |
| 4670 | ✗ | res.node = cast<IVisitable*>(node); | |
| 4671 | ✗ | return exprResultAny(res); | |
| 4672 | } | ||
| 4673 | |||
| 4674 | // Visit array items | ||
| 4675 | 160 | bool canBeConstant = true; | |
| 4676 | 160 | Vector<LLVMExprResult> itemResults; | |
| 4677 | 160 | itemResults.reserve(node.actualSize); | |
| 4678 | 1446 | foreach IVisitable* itemNode : node.itemLst.args { | |
| 4679 | 1286 | LLVMExprResult item = this.visitExpr(itemNode); | |
| 4680 | 1286 | canBeConstant = canBeConstant && !item.constant.isNull(); | |
| 4681 | 1286 | item.node = itemNode; | |
| 4682 | 1286 | itemResults.pushBack(item); | |
| 4683 | 1286 | } | |
| 4684 | |||
| 4685 | // Get LLVM type of item and array | ||
| 4686 | 160 | assert !itemResults.isEmpty(); | |
| 4687 | 160 | const QualType firstItemSTy = this.getEvaluatedType(node.itemLst.args.front()); | |
| 4688 | 160 | const llvm::Type itemType = firstItemSTy.toLLVMType(this.sourceFile); | |
| 4689 | 160 | const llvm::Type arrayType = llvm::getArrayType(itemType, node.actualSize); | |
| 4690 | |||
| 4691 | 160 | if canBeConstant { // All items are constants, so we can create a global constant array | |
| 4692 | // Collect constants | ||
| 4693 | 152 | Vector<llvm::Value> constants; | |
| 4694 | 1416 | foreach const LLVMExprResult& exprResult : itemResults { | |
| 4695 | // Delete potential constant globals, that were already created a layer below | ||
| 4696 | 1264 | const llvm::Type constantType = exprResult.constant.getType(); | |
| 4697 | 1264 | if constantType.isArrayTy() { | |
| 4698 | 22 | const String nestedGlobalName = exprResult.ptr.getName(); | |
| 4699 | 22 | llvm::Value nestedGlobal = this.module.getNamedGlobal(nestedGlobalName.getRaw()); | |
| 4700 | 22 | nestedGlobal.eraseGlobalFromParent(); | |
| 4701 | 22 | } | |
| 4702 | 1264 | constants.pushBack(exprResult.constant); | |
| 4703 | 1264 | } | |
| 4704 | |||
| 4705 | // Create global array | ||
| 4706 | 152 | const llvm::Value constantArray = llvm::getConstArray(itemType, constants); | |
| 4707 | 152 | const llvm::Value arrayAddr = this.createGlobalConst(String(ANON_GLOBAL_ARRAY_NAME), constantArray); | |
| 4708 | |||
| 4709 | 152 | LLVMExprResult res; | |
| 4710 | 152 | res.constant = constantArray; | |
| 4711 | 152 | res.ptr = arrayAddr; | |
| 4712 | 304 | return exprResultAny(res); | |
| 4713 | 152 | } | |
| 4714 | |||
| 4715 | // We have non-immediate values as items, so we need to take normal arrays as fallback | ||
| 4716 | 8 | const llvm::Value arrayAddr = this.insertAlloca(arrayType); | |
| 4717 | |||
| 4718 | // Retrieve address of first item | ||
| 4719 | 8 | const llvm::Value firstItemAddress = this.insertInBoundsGEP(arrayType, arrayAddr, this.getInt64(0l)); | |
| 4720 | |||
| 4721 | // Store all array items at their corresponding offsets | ||
| 4722 | 8 | llvm::Value currentItemAddress = firstItemAddress; | |
| 4723 | 30 | for unsigned long i = 0l; i < itemResults.getSize(); i++ { | |
| 4724 | 22 | LLVMExprResult& exprResult = itemResults.get(i); | |
| 4725 | 22 | const llvm::Value itemValue = this.resolveValue(exprResult.node, exprResult); | |
| 4726 | // Retrieve current item address | ||
| 4727 | 22 | if i >= 1l { | |
| 4728 | 14 | currentItemAddress = this.insertInBoundsGEP(itemType, currentItemAddress, this.getInt64(1l)); | |
| 4729 | 14 | } | |
| 4730 | // Store the item value | ||
| 4731 | 22 | const bool storeVolatile = exprResult.entry != nil<const SymbolTableEntry*> && exprResult.entry.isVolatile; | |
| 4732 | 22 | this.insertStore(itemValue, currentItemAddress, storeVolatile); | |
| 4733 | 22 | } | |
| 4734 | |||
| 4735 | 16 | return exprResultAny(exprResultWithPtr(arrayAddr)); | |
| 4736 | 160 | } | |
| 4737 | |||
| 4738 | 1662 | public f<Any> IRGenerator.visitStructInstantiation(ASTStructInstantiationNode* node) { | |
| 4739 | // Get struct object | ||
| 4740 | 1662 | const Struct* spiceStruct = node.instantiatedStructs.get(this.manIdx); | |
| 4741 | 1662 | assert spiceStruct != nil<const Struct*>; | |
| 4742 | 1662 | const QualTypeList& fieldTypes = spiceStruct.fieldTypes; | |
| 4743 | |||
| 4744 | // Can only be constant if none of the fields is of type reference | ||
| 4745 | 1662 | bool canBeConstant = !spiceStruct.hasReferenceFields(); | |
| 4746 | |||
| 4747 | // Get struct type | ||
| 4748 | 1662 | assert spiceStruct.entry != nil<SymbolTableEntry*>; | |
| 4749 | 1662 | const QualType& structEntryType = spiceStruct.entry.getQualType(); | |
| 4750 | 1662 | const llvm::Type structType = structEntryType.toLLVMType(this.sourceFile); | |
| 4751 | 1662 | assert !structType.isNull(); | |
| 4752 | |||
| 4753 | 1662 | if node.fieldLst == nil<ASTArgLstNode*> { | |
| 4754 | 44 | const llvm::Value constantStruct = this.getDefaultValueForSymbolType(structEntryType); | |
| 4755 | 44 | return exprResultAny(exprResultWithConstant(constantStruct)); | |
| 4756 | } | ||
| 4757 | |||
| 4758 | // Visit struct field values | ||
| 4759 | 1618 | Vector<LLVMExprResult> fieldValueResults; | |
| 4760 | 1618 | fieldValueResults.reserve(spiceStruct.fieldTypes.getSize()); | |
| 4761 | 5510 | foreach IVisitable* fieldValueNode : node.fieldLst.args { | |
| 4762 | 3892 | LLVMExprResult fieldValue = this.visitExpr(fieldValueNode); | |
| 4763 | 3892 | fieldValue.node = fieldValueNode; | |
| 4764 | 3892 | fieldValueResults.pushBack(fieldValue); | |
| 4765 | 3892 | canBeConstant = canBeConstant && !fieldValue.constant.isNull(); | |
| 4766 | 3892 | } | |
| 4767 | |||
| 4768 | 1618 | if canBeConstant { // All field values are constants, so we can create a global constant struct instantiation | |
| 4769 | // Collect constants | ||
| 4770 | 184 | Vector<llvm::Value> constants; | |
| 4771 | // The VTable address point for the synthesized vtable pointer | ||
| 4772 | 184 | if spiceStruct.hasSynthesizedVTablePtr() { | |
| 4773 | 6 | constants.pushBack(this.getVTableAddressPoint(&spiceStruct.structBase)); | |
| 4774 | 6 | } | |
| 4775 | // For each interface a nullptr | ||
| 4776 | 184 | foreach const QualType& interfaceType : spiceStruct.interfaceTypes { | |
| 4777 | ✗ | constants.pushBack(this.getDefaultValueForSymbolType(interfaceType)); | |
| 4778 | ✗ | } | |
| 4779 | // Constant value for each field | ||
| 4780 | 548 | foreach const LLVMExprResult& exprResult : fieldValueResults { | |
| 4781 | 364 | constants.pushBack(exprResult.constant); | |
| 4782 | 364 | } | |
| 4783 | |||
| 4784 | // Create global constant struct | ||
| 4785 | 184 | const llvm::Value constantStruct = llvm::getConstNamedStruct(structType, constants); | |
| 4786 | |||
| 4787 | 368 | return exprResultAny(exprResultWithConstant(constantStruct)); | |
| 4788 | 184 | } | |
| 4789 | |||
| 4790 | // We have at least one non-immediate value, so we need to take normal struct instantiation as fallback | ||
| 4791 | 1434 | const llvm::Value structAddr = this.insertAlloca(structType); | |
| 4792 | 1434 | const unsigned long interfaceCount = spiceStruct.interfaceTypes.getSize(); | |
| 4793 | 1434 | const unsigned long fieldCount = spiceStruct.fieldTypes.getSize(); | |
| 4794 | 1434 | unsigned long i = 0l; | |
| 4795 | |||
| 4796 | // Store the VTable address point for the synthesized vtable pointer | ||
| 4797 | 1434 | if spiceStruct.hasSynthesizedVTablePtr() { | |
| 4798 | 6 | this.insertStore(this.getVTableAddressPoint(&spiceStruct.structBase), structAddr); | |
| 4799 | 6 | } | |
| 4800 | |||
| 4801 | // Store interface values at their corresponding offsets | ||
| 4802 | 1434 | while i < interfaceCount { | |
| 4803 | ✗ | const QualType& interfaceType = spiceStruct.interfaceTypes.get(i); | |
| 4804 | // Get field value | ||
| 4805 | ✗ | const llvm::Value itemValue = this.getDefaultValueForSymbolType(interfaceType); | |
| 4806 | // Get field address | ||
| 4807 | ✗ | const llvm::Value currentFieldAddress = this.insertStructGEP(structType, structAddr, cast<unsigned int>(spiceStruct.getFieldElementIndex(i))); | |
| 4808 | // Store the item value | ||
| 4809 | ✗ | this.insertStore(itemValue, currentFieldAddress); | |
| 4810 | ✗ | i++; | |
| 4811 | ✗ | } | |
| 4812 | |||
| 4813 | // Store all field values at their corresponding offsets | ||
| 4814 | 1434 | const Vector<model::Function*>& fieldCopyCtors = node.fieldCopyCtors.get(this.manIdx); | |
| 4815 | 4962 | while i < interfaceCount + fieldCount { | |
| 4816 | 3528 | LLVMExprResult& exprResult = fieldValueResults.get(i); | |
| 4817 | 3528 | const QualType& fieldType = fieldTypes.get(i); | |
| 4818 | // Get field value | ||
| 4819 | 3528 | llvm::Value itemValue; | |
| 4820 | 3528 | const model::Function* copyCtor = fieldCopyCtors.get(i); | |
| 4821 | 3528 | if fieldType.isRef() { | |
| 4822 | 9 | itemValue = this.resolveAddress(exprResult); | |
| 4823 | 3528 | } else if copyCtor != nil<const model::Function*> { | |
| 4824 | // Deep-copy the value into the field via its copy ctor: the source is not consumed/moved here, so the | ||
| 4825 | // caller keeps its own copy alive and destructs it independently - a raw value copy would alias its | ||
| 4826 | // owned heap resources and lead to a double-free once one of the two is destructed. | ||
| 4827 | 28 | const llvm::Value originalPtr = this.resolveAddress(exprResult); | |
| 4828 | 28 | const llvm::Type fieldLLVMType = fieldType.toLLVMType(this.sourceFile); | |
| 4829 | 28 | const llvm::Value fieldCopyPtr = this.insertAlloca(fieldLLVMType, String("field.copy")); | |
| 4830 | 28 | Vector<llvm::Value> ctorArgs; | |
| 4831 | 28 | ctorArgs.pushBack(originalPtr); | |
| 4832 | 28 | this.generateCtorOrDtorCall(fieldCopyPtr, copyCtor, ctorArgs); | |
| 4833 | 28 | itemValue = this.insertLoad(fieldLLVMType, fieldCopyPtr); | |
| 4834 | 28 | } else { | |
| 4835 | 3491 | itemValue = this.resolveValue(exprResult.node, exprResult); | |
| 4836 | 3519 | } | |
| 4837 | // Get field address | ||
| 4838 | 3528 | const llvm::Value currentFieldAddress = this.insertStructGEP(structType, structAddr, cast<unsigned int>(spiceStruct.getFieldElementIndex(i))); | |
| 4839 | // Store the item value | ||
| 4840 | 3528 | const bool storeVolatile = exprResult.entry != nil<const SymbolTableEntry*> && exprResult.entry.isVolatile; | |
| 4841 | 3528 | this.insertStore(itemValue, currentFieldAddress, storeVolatile); | |
| 4842 | 3528 | i++; | |
| 4843 | 3528 | } | |
| 4844 | |||
| 4845 | // Attach address to anonymous symbol to keep track of de-allocation | ||
| 4846 | 1434 | const SymbolTableEntry* returnSymbol = this.currentScope.symbolTable.lookupAnonymous(cast<ASTNode*>(node)); | |
| 4847 | 1434 | if returnSymbol != nil<const SymbolTableEntry*> { | |
| 4848 | 10 | this.updateAddress(returnSymbol, structAddr); | |
| 4849 | 10 | } | |
| 4850 | |||
| 4851 | 2868 | return exprResultAny(exprResultWithPtr(structAddr)); | |
| 4852 | 1618 | } | |
| 4853 | |||
| 4854 | /** | ||
| 4855 | * Shared implementation of visitLambdaFunc, visitLambdaProc and visitLambdaExpr. The differences are marked below. | ||
| 4856 | * | ||
| 4857 | * @param node Lambda node | ||
| 4858 | * @param lambdaBase Lambda base node | ||
| 4859 | * @param spiceFunc Manifestation of the lambda (a copy, since the mangle suffix may be set) | ||
| 4860 | * @param isFunction Whether the lambda returns a value | ||
| 4861 | * @param body Body of the lambda (statement list), or nil for lambda expressions | ||
| 4862 | * @param lambdaExpr Expression of a lambda expression, or nil for lambda functions/procedures | ||
| 4863 | * @param setMangleSuffix Whether to set the mangle suffix to the manifestation index (lambda functions/procedures) | ||
| 4864 | * @return Fat pointer to the lambda | ||
| 4865 | */ | ||
| 4866 | 136 | f<Any> IRGenerator.visitLambda(ASTNode* node, ASTLambdaBaseNode* lambdaBase, model::Function& spiceFunc, bool isFunction, IVisitable* body, IVisitable* lambdaExpr, bool setMangleSuffix, const String& scopeId) { | |
| 4867 | 136 | ParamInfoList paramInfoList; | |
| 4868 | 136 | Vector<llvm::Type> paramTypes; | |
| 4869 | |||
| 4870 | // Change scope | ||
| 4871 | 136 | Scope* bodyScope = this.currentScope.getChildScope(scopeId); | |
| 4872 | 136 | this.currentScope = bodyScope; | |
| 4873 | |||
| 4874 | // Every lambda uniformly takes a trailing capture-struct pointer as its last argument, even when it captures | ||
| 4875 | // nothing. This keeps the calling convention of all lambdas (and plain function pointers) identical, so a lambda | ||
| 4876 | // can be stored, retrieved and called without the call site knowing statically whether it captures. A | ||
| 4877 | // non-capturing lambda simply ignores the passed (poison) pointer. Passing captures last (rather than first) keeps | ||
| 4878 | // fat ptr slot 0 directly callable through a plain C calling convention (e.g. when handed to a C API as a callback). | ||
| 4879 | 136 | const CaptureMap& captures = bodyScope.symbolTable.captures; | |
| 4880 | 136 | const bool hasCaptures = !captures.isEmpty(); | |
| 4881 | 136 | llvm::Type capturesStructType; | |
| 4882 | 136 | if hasCaptures { | |
| 4883 | 68 | capturesStructType = this.buildCapturesContainerType(captures); | |
| 4884 | 68 | } | |
| 4885 | |||
| 4886 | // Visit parameters | ||
| 4887 | 136 | unsigned long argIdx = 0l; | |
| 4888 | 136 | if lambdaBase.hasParams { | |
| 4889 | 89 | const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(lambdaBase.paramLst); | |
| 4890 | 89 | const unsigned long numOfParams = spiceFunc.paramList.getSize(); | |
| 4891 | 89 | const QualTypeList spiceFuncParamTypes = spiceFunc.getParamTypes(); | |
| 4892 | 89 | paramInfoList.reserve(numOfParams + 1l); | |
| 4893 | 89 | paramTypes.reserve(numOfParams + 1l); | |
| 4894 | 204 | while argIdx < numOfParams { | |
| 4895 | 115 | const ASTDeclStmtNode* param = paramLst.params.get(argIdx); | |
| 4896 | // Get symbol table entry of param | ||
| 4897 | 115 | const SymbolTableEntry* paramSymbol = this.currentScope.lookupStrict(param.varName); | |
| 4898 | 115 | assert paramSymbol != nil<const SymbolTableEntry*>; | |
| 4899 | // Retrieve type of param | ||
| 4900 | 115 | const QualType& paramSType = spiceFuncParamTypes.get(argIdx); | |
| 4901 | 115 | const llvm::Type paramType = paramSType.getParamLLVMType(this.sourceFile); | |
| 4902 | // Add it to the lists | ||
| 4903 | 115 | paramInfoList.pushBack(ParamInfo{param.varName, paramSymbol}); | |
| 4904 | 115 | paramTypes.pushBack(paramType); | |
| 4905 | 115 | argIdx++; | |
| 4906 | 115 | } | |
| 4907 | 89 | } | |
| 4908 | |||
| 4909 | // Append the trailing capture-struct pointer (always passed as pointer) | ||
| 4910 | 136 | paramInfoList.pushBack(ParamInfo{String(CAPTURES_PARAM_NAME), nil<const SymbolTableEntry*>}); | |
| 4911 | 136 | paramTypes.pushBack(this.builder.getPtrTy()); | |
| 4912 | 136 | const QualType returnSType = isFunction ? spiceFunc.returnType : QualType(SuperType::TY_DYN); | |
| 4913 | 136 | const unsigned int argOffset = this.getArgOffset(returnSType); | |
| 4914 | 136 | const unsigned int capturesArgIdx = argOffset + cast<unsigned int>(paramTypes.getSize()) - 1; | |
| 4915 | |||
| 4916 | // Get return type | ||
| 4917 | 136 | llvm::Type returnType = this.builder.getVoidTy(); | |
| 4918 | 136 | if isFunction { | |
| 4919 | 65 | returnType = spiceFunc.returnType.toLLVMType(this.sourceFile); | |
| 4920 | 65 | } | |
| 4921 | |||
| 4922 | // Create function or implement declared function | ||
| 4923 | 136 | if setMangleSuffix { | |
| 4924 | 132 | spiceFunc.mangleSuffix = "." + toString(this.manIdx); | |
| 4925 | 132 | } | |
| 4926 | 136 | const String mangledName = spiceFunc.getMangledName(); | |
| 4927 | 136 | const llvm::Type funcType = this.getFunctionType(returnSType, paramTypes); | |
| 4928 | 136 | this.module.getOrInsertFunction(mangledName.getRaw(), funcType); | |
| 4929 | 136 | llvm::Function lambda = this.module.getFunction(mangledName.getRaw()); | |
| 4930 | |||
| 4931 | // Set attributes to function | ||
| 4932 | 136 | lambda.setLinkage(llvm::LLVMLinkage::InternalLinkage); | |
| 4933 | 136 | this.addCommonFctAttrs(lambda); | |
| 4934 | 136 | this.enableFunctionInstrumentation(lambda); | |
| 4935 | 136 | const ReturnABIInfo returnABI = this.getReturnABIInfo(returnSType); | |
| 4936 | 136 | if returnABI.isIndirect() { | |
| 4937 | 5 | this.addSRetParamAttrs(lambda, returnABI.memoryType); | |
| 4938 | 5 | } | |
| 4939 | |||
| 4940 | // In case of captures, add attribute to captures argument | ||
| 4941 | 136 | if hasCaptures { | |
| 4942 | 68 | lambda.addParamAttr(capturesArgIdx, "noundef"); | |
| 4943 | 68 | lambda.addParamAttr(capturesArgIdx, "nonnull"); | |
| 4944 | 68 | lambda.addDereferenceableParamAttr(capturesArgIdx, cast<unsigned long>(this.module.getPointerSize())); | |
| 4945 | 68 | } | |
| 4946 | |||
| 4947 | // Add debug info | ||
| 4948 | 136 | this.diGenerator.generateFunctionDebugInfo(lambda, &spiceFunc, true); | |
| 4949 | 136 | this.diGenerator.setSourceLocation(node); | |
| 4950 | |||
| 4951 | // Save alloca insert markers | ||
| 4952 | 136 | const llvm::BasicBlock allocaInsertBlockOrig = this.allocaInsertBlock; | |
| 4953 | 136 | const llvm::Value allocaInsertInstOrig = this.allocaInsertInst; | |
| 4954 | 136 | const llvm::BasicBlock bOrig = this.builder.getInsertBlock(); | |
| 4955 | |||
| 4956 | // Create entry block | ||
| 4957 | 136 | const llvm::BasicBlock bEntry = this.createBlock(); | |
| 4958 | 136 | this.switchToBlock(bEntry, lambda); | |
| 4959 | |||
| 4960 | // Reset alloca insert markers to this block | ||
| 4961 | 136 | this.allocaInsertBlock = bEntry; | |
| 4962 | 136 | this.allocaInsertInst = llvm::Value(); | |
| 4963 | |||
| 4964 | // Declare result variable | ||
| 4965 | 136 | const SymbolTableEntry* resultEntry = nil<const SymbolTableEntry*>; | |
| 4966 | 136 | if body != nil<IVisitable*> && isFunction { | |
| 4967 | 61 | resultEntry = this.currentScope.lookupStrict(String(RETURN_VARIABLE_NAME)); | |
| 4968 | 61 | assert resultEntry != nil<const SymbolTableEntry*>; | |
| 4969 | 61 | if resultEntry.isInitialized() { | |
| 4970 | // If the value is returned via memory, the result variable lives in the caller's memory | ||
| 4971 | ✗ | llvm::Value resultAddr = getSRetArg(lambda); | |
| 4972 | ✗ | if resultAddr.isNull() { | |
| 4973 | ✗ | resultAddr = this.insertAlloca(returnType, String(RETURN_VARIABLE_NAME)); | |
| 4974 | ✗ | } | |
| 4975 | ✗ | this.updateAddress(resultEntry, resultAddr); | |
| 4976 | // Generate debug info | ||
| 4977 | ✗ | this.diGenerator.generateLocalVarDebugInfo(String(RETURN_VARIABLE_NAME), resultAddr); | |
| 4978 | ✗ | } | |
| 4979 | 61 | } | |
| 4980 | |||
| 4981 | // Store function argument values. The sret parameter is skipped | ||
| 4982 | 136 | llvm::Value captureStructPtrPtr; | |
| 4983 | 136 | const unsigned int argCount = lambda.argSize() - argOffset; | |
| 4984 | 136 | const Vector<llvm::Type> funcParamTypes = funcType.getParamTypes(); | |
| 4985 | 387 | for unsigned int argNumber = 0; argNumber < argCount; argNumber++ { | |
| 4986 | 251 | const llvm::Value arg = lambda.getArg(argOffset + argNumber); | |
| 4987 | // Get parameter info | ||
| 4988 | 251 | const ParamInfo& paramInfo = paramInfoList.get(argNumber); | |
| 4989 | 251 | const SymbolTableEntry* paramSymbol = paramInfo.entry; | |
| 4990 | // Decayed array params already carry the address of the array, so they do not need a local copy | ||
| 4991 | 251 | if this.bindDecayedArrayParam(arg, argNumber, paramInfo.name, paramSymbol) { | |
| 4992 | 2 | continue; | |
| 4993 | } | ||
| 4994 | // Allocate space for it | ||
| 4995 | 249 | const llvm::Type paramType = funcParamTypes.get(argOffset + argNumber); | |
| 4996 | 249 | const llvm::Value paramAddress = this.insertAlloca(paramType, paramInfo.name); | |
| 4997 | // Update the symbol table entry | ||
| 4998 | 249 | const bool isCapturesStruct = argOffset + argNumber == capturesArgIdx; | |
| 4999 | 249 | if isCapturesStruct { | |
| 5000 | 136 | captureStructPtrPtr = paramAddress; | |
| 5001 | 136 | } else { | |
| 5002 | 113 | this.updateAddress(paramSymbol, paramAddress); | |
| 5003 | 113 | } | |
| 5004 | // Generate debug info | ||
| 5005 | 249 | if !isCapturesStruct { | |
| 5006 | 113 | this.diGenerator.generateLocalVarDebugInfo(paramInfo.name, paramAddress, cast<unsigned long>(argNumber) + 1l); | |
| 5007 | 113 | } | |
| 5008 | // Store the value at the new address | ||
| 5009 | 249 | this.insertStore(arg, paramAddress); | |
| 5010 | 249 | } | |
| 5011 | |||
| 5012 | // Store the default values for optional function args | ||
| 5013 | 136 | if lambdaBase.paramLst != nil<IVisitable*> { | |
| 5014 | 89 | const ASTParamLstNode* paramLst = downcast<ASTParamLstNode>(lambdaBase.paramLst); | |
| 5015 | 89 | while argIdx < paramLst.params.getSize() { | |
| 5016 | ✗ | this.visit(cast<IVisitable*>(paramLst.params.get(argIdx))); | |
| 5017 | ✗ | argIdx++; | |
| 5018 | ✗ | } | |
| 5019 | 89 | } | |
| 5020 | |||
| 5021 | // Extract captures from captures struct | ||
| 5022 | 136 | if hasCaptures { | |
| 5023 | 68 | assert !paramInfoList.isEmpty(); | |
| 5024 | 68 | this.unpackCapturesToLocalVariables(captures, captureStructPtrPtr, capturesStructType); | |
| 5025 | 68 | } | |
| 5026 | |||
| 5027 | 136 | if body != nil<IVisitable*> { | |
| 5028 | // Visit body | ||
| 5029 | 132 | this.visit(body); | |
| 5030 | |||
| 5031 | // Create return statement if the block is not terminated yet | ||
| 5032 | 132 | if !this.blockAlreadyTerminated { | |
| 5033 | 70 | if isFunction { | |
| 5034 | ✗ | llvm::Value res = this.getDefaultValueForSymbolType(spiceFunc.returnType); | |
| 5035 | ✗ | if resultEntry.isInitialized() { | |
| 5036 | ✗ | const llvm::Value resultAddr = this.getAddress(resultEntry); | |
| 5037 | ✗ | res = resultAddr.isSameAs(getSRetArg(lambda)) ? llvm::Value() : this.insertLoad(returnType, resultAddr); | |
| 5038 | ✗ | } | |
| 5039 | ✗ | this.insertReturn(res); | |
| 5040 | ✗ | } else { | |
| 5041 | 70 | this.builder.createRetVoid(); | |
| 5042 | 70 | } | |
| 5043 | 70 | } | |
| 5044 | 132 | } else { | |
| 5045 | // Visit lambda expression | ||
| 5046 | 4 | const llvm::Value exprResult = this.resolveValue(lambdaExpr); | |
| 5047 | 4 | this.insertReturn(exprResult); | |
| 5048 | 4 | } | |
| 5049 | |||
| 5050 | // Pop capture addresses | ||
| 5051 | 136 | if hasCaptures { | |
| 5052 | 160 | foreach Pair<const String&, const Capture&> capture : captures { | |
| 5053 | 92 | const Capture& captureValue = capture.getSecond(); | |
| 5054 | 92 | this.popAddress(captureValue.capturedSymbol); | |
| 5055 | 92 | } | |
| 5056 | 68 | } | |
| 5057 | |||
| 5058 | // Conclude debug info for function | ||
| 5059 | 136 | this.diGenerator.concludeFunctionDebugInfo(); | |
| 5060 | 136 | this.diGenerator.setSourceLocation(node); | |
| 5061 | |||
| 5062 | // Restore alloca insert markers | ||
| 5063 | 136 | this.builder.setInsertPoint(bOrig); | |
| 5064 | 136 | this.blockAlreadyTerminated = false; | |
| 5065 | 136 | this.allocaInsertBlock = allocaInsertBlockOrig; | |
| 5066 | 136 | this.allocaInsertInst = allocaInsertInstOrig; | |
| 5067 | |||
| 5068 | // Change back to original scope | ||
| 5069 | 136 | this.currentScope = this.currentScope.parent; | |
| 5070 | |||
| 5071 | // Verify function | ||
| 5072 | 136 | this.verifyFunction(lambda, node.codeLoc); | |
| 5073 | |||
| 5074 | // Captures, create a struct { <fct-ptr>, <capture struct ptr> } | ||
| 5075 | 136 | const llvm::Value res = this.buildFatFctPtr(bodyScope, capturesStructType, lambda.asValue()); | |
| 5076 | |||
| 5077 | 136 | LLVMExprResult exprResult; | |
| 5078 | 136 | exprResult.ptr = res; | |
| 5079 | 136 | exprResult.node = node.visitable; | |
| 5080 | 272 | return exprResultAny(exprResult); | |
| 5081 | 136 | } | |
| 5082 | |||
| 5083 | 61 | public f<Any> IRGenerator.visitLambdaFunc(ASTLambdaFuncNode* node) { | |
| 5084 | 61 | model::Function spiceFunc = node.manifestations.get(this.manIdx); | |
| 5085 | 122 | return this.visitLambda(cast<ASTNode*>(node), &node.node, spiceFunc, true, node.body, nil<IVisitable*>, true, node.getScopeId()); | |
| 5086 | 61 | } | |
| 5087 | |||
| 5088 | 71 | public f<Any> IRGenerator.visitLambdaProc(ASTLambdaProcNode* node) { | |
| 5089 | 71 | model::Function spiceFunc = node.manifestations.get(this.manIdx); | |
| 5090 | 142 | return this.visitLambda(cast<ASTNode*>(node), &node.node, spiceFunc, false, node.body, nil<IVisitable*>, true, node.getScopeId()); | |
| 5091 | 71 | } | |
| 5092 | |||
| 5093 | 4 | public f<Any> IRGenerator.visitLambdaExpr(ASTLambdaExprNode* node) { | |
| 5094 | 4 | model::Function spiceFunc = node.manifestations.get(this.manIdx); | |
| 5095 | 8 | return this.visitLambda(cast<ASTNode*>(node), &node.node, spiceFunc, spiceFunc.isFunction(), nil<IVisitable*>, node.lambdaExpr, false, node.getScopeId()); | |
| 5096 | 4 | } | |
| 5097 | |||
| 5098 | 10048 | public f<Any> IRGenerator.visitDataType(ASTDataTypeNode* node) { | |
| 5099 | // Retrieve symbol type | ||
| 5100 | 10048 | const QualType symbolType = this.getEvaluatedType(cast<IVisitable*>(node)); | |
| 5101 | 10048 | assert !symbolType.is(SuperType::TY_DYN); // Symbol type should not be dyn anymore at this point | |
| 5102 | 10048 | return Any(symbolType.toLLVMType(this.sourceFile)); | |
| 5103 | } | ||
| 5104 | |||
| 5105 | 64 | f<llvm::Function> IRGenerator.getOrCreateFatFctPtrThunk(llvm::Function target) { | |
| 5106 | // Plain function/procedure references are stored in fat function pointers and called through the uniform lambda | ||
| 5107 | // calling convention, which always passes a trailing capture-struct pointer. A named function does not have that | ||
| 5108 | // parameter, so we wrap it in a thunk that has the extra (ignored) pointer and forwards to the real function. | ||
| 5109 | 64 | const String thunkName = target.getName() + ".fatthunk"; | |
| 5110 | 64 | const llvm::Function existing = this.module.getFunction(thunkName.getRaw()); | |
| 5111 | 64 | if !existing.isNull() { | |
| 5112 | 42 | return existing; | |
| 5113 | } | ||
| 5114 | |||
| 5115 | // Build the thunk signature: the target's signature with an additional trailing capture-struct pointer | ||
| 5116 | 43 | const llvm::Type targetType = target.getFunctionType(); | |
| 5117 | 43 | const Vector<llvm::Type> targetParamTypes = targetType.getParamTypes(); | |
| 5118 | 43 | Vector<llvm::Type> paramTypes; | |
| 5119 | 43 | paramTypes.reserve(targetParamTypes.getSize() + 1l); | |
| 5120 | 136 | foreach const llvm::Type& paramType : targetParamTypes { | |
| 5121 | 93 | paramTypes.pushBack(paramType); | |
| 5122 | 93 | } | |
| 5123 | 43 | paramTypes.pushBack(this.builder.getPtrTy()); // Ignored captures pointer | |
| 5124 | 43 | const llvm::Type thunkType = llvm::getFunctionType(targetType.getReturnType(), paramTypes, targetType.isFunctionVarArg()); | |
| 5125 | |||
| 5126 | 43 | const llvm::Function thunk = this.module.createFunction(thunkType, llvm::LLVMLinkage::PrivateLinkage, thunkName.getRaw()); | |
| 5127 | 43 | this.addCommonFctAttrs(thunk); | |
| 5128 | |||
| 5129 | // Save insert markers, because we emit the thunk body in the middle of generating another function | ||
| 5130 | 43 | const llvm::BasicBlock bOrig = this.builder.getInsertBlock(); | |
| 5131 | 43 | const llvm::BasicBlock allocaInsertBlockOrig = this.allocaInsertBlock; | |
| 5132 | 43 | const llvm::Value allocaInsertInstOrig = this.allocaInsertInst; | |
| 5133 | |||
| 5134 | 43 | const llvm::BasicBlock bEntry = this.createBlock(String("entry")); | |
| 5135 | 43 | this.switchToBlock(bEntry, thunk); | |
| 5136 | |||
| 5137 | // Forward all arguments except the trailing (ignored) captures pointer | ||
| 5138 | 43 | Vector<llvm::Value> fwdArgs; | |
| 5139 | 43 | fwdArgs.reserve(targetParamTypes.getSize()); | |
| 5140 | 136 | for unsigned int i = 0; i < cast<unsigned int>(targetParamTypes.getSize()); i++ { | |
| 5141 | 93 | fwdArgs.pushBack(thunk.getArg(i)); | |
| 5142 | 93 | } | |
| 5143 | 43 | const llvm::Value call = this.builder.createCall(target, fwdArgs); | |
| 5144 | // Forward the sret pointer, if the target returns via memory | ||
| 5145 | 43 | const llvm::Value targetSRetArg = getSRetArg(target); | |
| 5146 | 43 | if !targetSRetArg.isNull() { | |
| 5147 | 1 | const llvm::Type sretType = target.getParamTypeAttr(0, "sret"); | |
| 5148 | 1 | this.addSRetParamAttrs(thunk, sretType); | |
| 5149 | 1 | this.addSRetCallParamAttrs(call, sretType); | |
| 5150 | 1 | } | |
| 5151 | 43 | const llvm::Type targetReturnType = targetType.getReturnType(); | |
| 5152 | 43 | if targetReturnType.isVoidTy() { | |
| 5153 | 20 | this.builder.createRetVoid(); | |
| 5154 | 20 | } else { | |
| 5155 | 23 | this.builder.createRet(call); | |
| 5156 | 23 | } | |
| 5157 | |||
| 5158 | // Restore insert markers | ||
| 5159 | 43 | this.builder.setInsertPoint(bOrig); | |
| 5160 | 43 | this.blockAlreadyTerminated = false; | |
| 5161 | 43 | this.allocaInsertBlock = allocaInsertBlockOrig; | |
| 5162 | 43 | this.allocaInsertInst = allocaInsertInstOrig; | |
| 5163 | |||
| 5164 | 86 | return thunk; | |
| 5165 | 64 | } | |
| 5166 | |||
| 5167 | 200 | f<llvm::Value> IRGenerator.buildFatFctPtr(Scope* bodyScope, llvm::Type capturesStructType, llvm::Value lambda) { | |
| 5168 | // Create capture struct if required | ||
| 5169 | 200 | llvm::Value capturesPtr; | |
| 5170 | // Byte size of the owned capture struct. This is only non-zero if the captures live in a dedicated | ||
| 5171 | // (stack-allocated) struct that the std Lambda type needs to relocate to the heap to take ownership. | ||
| 5172 | // A single capture stored inline in the capturePtr slot, or no captures at all, leaves this at 0. | ||
| 5173 | 200 | unsigned long captureStructSize = 0l; | |
| 5174 | 200 | if !capturesStructType.isNull() { | |
| 5175 | 68 | assert bodyScope != nil<Scope*>; | |
| 5176 | 68 | const CaptureMap& captures = bodyScope.symbolTable.captures; | |
| 5177 | // If we have a single capture of ptr type, we can directly store it into the fat ptr. Otherwise, we need a stack allocated | ||
| 5178 | // struct to store the captures in a memory-efficient manner and store a pointer to that struct to the fat ptr. | ||
| 5179 | 68 | if capturesStructType.isPointerTy() { | |
| 5180 | 39 | assert captures.getSize() == 1l; | |
| 5181 | 78 | foreach Pair<const String&, const Capture&> captureEntry : captures { | |
| 5182 | 39 | const Capture& capture = captureEntry.getSecond(); | |
| 5183 | 39 | if capture.getMode() == CapturePassMode::BY_VALUE { | |
| 5184 | 22 | const QualType& capturedType = capture.capturedSymbol.getQualType(); | |
| 5185 | 22 | const llvm::Type varType = capturedType.toLLVMType(this.sourceFile); | |
| 5186 | 22 | capturesPtr = this.insertLoad(varType, this.getAddress(capture.capturedSymbol)); | |
| 5187 | 22 | } else { | |
| 5188 | 17 | capturesPtr = this.getAddress(capture.capturedSymbol); | |
| 5189 | 17 | } | |
| 5190 | 39 | } | |
| 5191 | 39 | } else { | |
| 5192 | 29 | capturesPtr = this.insertAlloca(capturesStructType, String(CAPTURES_PARAM_NAME)); | |
| 5193 | 29 | captureStructSize = this.module.getTypeAllocSize(capturesStructType); | |
| 5194 | 29 | unsigned int captureIdx = 0; | |
| 5195 | 82 | foreach Pair<const String&, const Capture&> captureEntry : captures { | |
| 5196 | 53 | const Capture& capture = captureEntry.getSecond(); | |
| 5197 | 53 | const SymbolTableEntry* capturedEntry = capture.capturedSymbol; | |
| 5198 | // Get address or value of captured variable, depending on the capturing mode | ||
| 5199 | 53 | llvm::Value capturedValue = this.getAddress(capturedEntry); | |
| 5200 | 53 | assert !capturedValue.isNull(); | |
| 5201 | 53 | if capture.getMode() == CapturePassMode::BY_VALUE { | |
| 5202 | 38 | const QualType& capturedType = capturedEntry.getQualType(); | |
| 5203 | 38 | const llvm::Type captureType = capturedType.toLLVMType(this.sourceFile); | |
| 5204 | 38 | capturedValue = this.insertLoad(captureType, capturedValue); | |
| 5205 | 38 | } | |
| 5206 | // Store it in the capture struct | ||
| 5207 | 53 | const llvm::Value captureAddress = this.insertStructGEP(capturesStructType, capturesPtr, captureIdx); | |
| 5208 | 53 | this.insertStore(capturedValue, captureAddress); | |
| 5209 | 53 | captureIdx++; | |
| 5210 | 53 | } | |
| 5211 | 29 | } | |
| 5212 | 68 | } | |
| 5213 | |||
| 5214 | // Create fat pointer | ||
| 5215 | 200 | const llvm::Value fatFctPtr = this.insertAlloca(this.llvmTypes.lambdaFatPtrType, String("fat.ptr")); | |
| 5216 | 200 | const llvm::Value fctPtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatFctPtr, 0); | |
| 5217 | 200 | this.insertStore(lambda, fctPtr); | |
| 5218 | 200 | const llvm::Value capturePtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatFctPtr, 1); | |
| 5219 | // The uniform lambda calling convention always loads and passes this slot, so it must hold a defined value even | ||
| 5220 | // when there are no captures. A null pointer is passed to (and ignored by) non-capturing targets. | ||
| 5221 | 200 | this.insertStore(!capturesPtr.isNull() ? capturesPtr : llvm::getNullValue(this.builder.getPtrTy()), capturePtr); | |
| 5222 | 200 | const llvm::Value captureSizePtr = this.insertStructGEP(this.llvmTypes.lambdaFatPtrType, fatFctPtr, 2); | |
| 5223 | 200 | this.insertStore(this.builder.getInt64(captureStructSize), captureSizePtr); | |
| 5224 | |||
| 5225 | 400 | return fatFctPtr; | |
| 5226 | 200 | } | |
| 5227 | |||
| 5228 | 68 | f<llvm::Type> IRGenerator.buildCapturesContainerType(const CaptureMap& captures) { | |
| 5229 | 68 | assert !captures.isEmpty(); | |
| 5230 | |||
| 5231 | // If we have only one capture that is a ptr, we can just use that ptr type | ||
| 5232 | 68 | if captures.getSize() == 1l { | |
| 5233 | 49 | foreach Pair<const String&, const Capture&> captureEntry : captures { | |
| 5234 | 44 | const Capture& capture = captureEntry.getSecond(); | |
| 5235 | 44 | const QualType& capturedType = capture.capturedSymbol.getQualType(); | |
| 5236 | 44 | if capturedType.isPtr() || capture.getMode() == CapturePassMode::BY_REFERENCE { | |
| 5237 | 39 | return this.builder.getPtrTy(); | |
| 5238 | } | ||
| 5239 | 5 | } | |
| 5240 | 5 | } | |
| 5241 | |||
| 5242 | // Create captures struct type | ||
| 5243 | 29 | Vector<llvm::Type> captureTypes; | |
| 5244 | 82 | foreach Pair<const String&, const Capture&> captureEntry : captures { | |
| 5245 | 53 | const Capture& c = captureEntry.getSecond(); | |
| 5246 | 53 | if c.getMode() == CapturePassMode::BY_VALUE { | |
| 5247 | 38 | const QualType& capturedType = c.capturedSymbol.getQualType(); | |
| 5248 | 38 | captureTypes.pushBack(capturedType.toLLVMType(this.sourceFile)); | |
| 5249 | 38 | } else { | |
| 5250 | 15 | captureTypes.pushBack(this.builder.getPtrTy()); | |
| 5251 | 15 | } | |
| 5252 | 53 | } | |
| 5253 | 58 | return llvm::getStructType(*this.context, captureTypes); | |
| 5254 | 29 | } | |
| 5255 | |||
| 5256 | 68 | p IRGenerator.unpackCapturesToLocalVariables(const CaptureMap& captures, llvm::Value val, llvm::Type structType) { | |
| 5257 | 68 | assert !captures.isEmpty(); | |
| 5258 | // Debug info attached to a computed address gets dropped by the backend, so the debug info of all captures is attached | ||
| 5259 | // to the stack slot of the captures ptr (val) instead. The address ops lead from there to the capture. By reference | ||
| 5260 | // captures hold the address of the captured variable, so they need one more deref. | ||
| 5261 | // If we have only one capture that is a ptr, we can just load the ptr | ||
| 5262 | 68 | if captures.getSize() == 1l { | |
| 5263 | 49 | foreach Pair<const String&, const Capture&> captureEntry : captures { | |
| 5264 | 44 | const Capture& firstCapture = captureEntry.getSecond(); | |
| 5265 | 44 | const QualType& capturedType = firstCapture.capturedSymbol.getQualType(); | |
| 5266 | 44 | if capturedType.isPtr() || firstCapture.getMode() == CapturePassMode::BY_REFERENCE { | |
| 5267 | // Interpret capturesPtr as ptr to the first and only capture | ||
| 5268 | 39 | const llvm::Value captureAddress = val; | |
| 5269 | 39 | this.pushAddress(firstCapture.capturedSymbol, captureAddress); | |
| 5270 | // Generate debug info | ||
| 5271 | 39 | Vector<unsigned long> addressOps; | |
| 5272 | 39 | if firstCapture.getMode() == CapturePassMode::BY_REFERENCE { | |
| 5273 | 17 | addressOps.pushBack(llvm::DW_OP_DEREF); | |
| 5274 | 17 | } | |
| 5275 | 39 | this.diGenerator.generateLocalVarDebugInfo(firstCapture.getName(), val, ULONG_MAX_VALUE, addressOps); | |
| 5276 | 39 | return; | |
| 5277 | 39 | } | |
| 5278 | 5 | } | |
| 5279 | 5 | } | |
| 5280 | |||
| 5281 | // Interpret capturesPtr as ptr to the captures struct | ||
| 5282 | 29 | const llvm::Value capturesPtr = this.insertLoad(this.builder.getPtrTy(), val); | |
| 5283 | |||
| 5284 | 29 | unsigned int captureIdx = 0; | |
| 5285 | 82 | foreach Pair<const String&, const Capture&> captureEntry : captures { | |
| 5286 | 53 | const String& name = captureEntry.getFirst(); | |
| 5287 | 53 | const Capture& capture = captureEntry.getSecond(); | |
| 5288 | 53 | const String valueName = capture.getMode() == CapturePassMode::BY_REFERENCE ? name + ".addr" : name; | |
| 5289 | 53 | const llvm::Value captureAddress = this.insertStructGEP(structType, capturesPtr, captureIdx, valueName); | |
| 5290 | 53 | this.pushAddress(capture.capturedSymbol, captureAddress); | |
| 5291 | // Generate debug info | ||
| 5292 | 53 | const unsigned long offset = this.module.getStructElementOffset(structType, captureIdx); | |
| 5293 | 53 | Vector<unsigned long> addressOps; | |
| 5294 | 53 | addressOps.pushBack(llvm::DW_OP_DEREF); | |
| 5295 | 53 | addressOps.pushBack(llvm::DW_OP_PLUS_UCONST); | |
| 5296 | 53 | addressOps.pushBack(offset); | |
| 5297 | 53 | if capture.getMode() == CapturePassMode::BY_REFERENCE { | |
| 5298 | 15 | addressOps.pushBack(llvm::DW_OP_DEREF); | |
| 5299 | 15 | } | |
| 5300 | 53 | this.diGenerator.generateLocalVarDebugInfo(capture.getName(), val, ULONG_MAX_VALUE, addressOps); | |
| 5301 | 53 | captureIdx++; | |
| 5302 | 53 | } | |
| 5303 | 29 | } | |
| 5304 | |||
| 5305 | // ====================================================== Builtin functions ====================================================== | ||
| 5306 | |||
| 5307 | 9122 | f<Any> IRGenerator.visitBuiltinCall(ASTFctCallNode* node) { | |
| 5308 | // If we have a compile time value, but the computation is still there, we can simply use this constant value | ||
| 5309 | 9122 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 5310 | 9122 | if baseNode.hasCompileTimeValue(this.manIdx) { | |
| 5311 | 1112 | const CompileTimeValue compileTimeValue = baseNode.getCompileTimeValue(this.manIdx); | |
| 5312 | 1112 | const QualType evaluatedType = this.getEvaluatedType(cast<IVisitable*>(node)); | |
| 5313 | 1112 | const llvm::Value value = this.getConst(compileTimeValue, evaluatedType, baseNode); | |
| 5314 | 1112 | return exprResultAny(exprResultWithConstant(value)); | |
| 5315 | } | ||
| 5316 | |||
| 5317 | // If we need to perform runtime actions, call the specified IRGenerator delegate | ||
| 5318 | 8010 | const String& name = node.fqFunctionName; | |
| 5319 | 8010 | if name == BUILTIN_FCT_NAME_PRINTF { return this.visitBuiltinPrintfCall(node); } | |
| 5320 | 5673 | if name == BUILTIN_FCT_NAME_LEN { return this.visitBuiltinLenCall(node); } | |
| 5321 | 5574 | if name == BUILTIN_FCT_NAME_PANIC { return this.visitBuiltinPanicCall(node); } | |
| 5322 | 2641 | if name == BUILTIN_FCT_NAME_SYSCALL { return this.visitBuiltinSyscallCall(node); } | |
| 5323 | 2636 | if name == BUILTIN_FCT_NAME_NEW { return this.visitBuiltinNewCall(node); } | |
| 5324 | 2398 | if name == BUILTIN_FCT_NAME_PLACEMENT_NEW { return this.visitBuiltinPlacementNewCall(node); } | |
| 5325 | 1876 | if name == BUILTIN_FCT_NAME_ERR_TRACE_BUFFER { return this.visitBuiltinErrTraceBufferCall(node); } | |
| 5326 | 952 | if name == BUILTIN_FCT_NAME_STDERR { return this.visitBuiltinStdErrCall(node); } | |
| 5327 | ✗ | if name == BUILTIN_FCT_NAME_FRAME_ADDRESS { return this.visitBuiltinFrameAddressCall(node); } | |
| 5328 | − | panic(Error("Builtin function not implemented!")); // LCOV_EXCL_LINE | |
| 5329 | } | ||
| 5330 | |||
| 5331 | 2337 | f<Any> IRGenerator.visitBuiltinPrintfCall(ASTFctCallNode* node) { | |
| 5332 | // Retrieve templated string | ||
| 5333 | 2337 | assert node.hasArgs; | |
| 5334 | 2337 | IVisitable* firstArg = node.argLst.args.front(); | |
| 5335 | 2337 | const QualType firstArgType = this.getEvaluatedType(firstArg); | |
| 5336 | 2337 | assert firstArgType.is(SuperType::TY_STRING) && this.hasCompileTimeValue(firstArg); | |
| 5337 | 2337 | const CompileTimeValue firstArgValue = this.getCompileTimeValue(firstArg); | |
| 5338 | 2337 | const unsigned long stringOffset = firstArgValue.getStringValueOffset(); | |
| 5339 | 2337 | const String templatedString = this.resourceManager.compileTimeStringValues.get(stringOffset); | |
| 5340 | |||
| 5341 | // Push the template string as first argument | ||
| 5342 | 2337 | Vector<llvm::Value> printfArgs; | |
| 5343 | 2337 | const llvm::Value templateString = this.createGlobalStringConst(String("printf.str."), templatedString, node.codeLoc); | |
| 5344 | 2337 | printfArgs.pushBack(templateString); | |
| 5345 | |||
| 5346 | // Collect replacement arguments | ||
| 5347 | 4390 | for unsigned long argIdx = 1l; argIdx < node.argLst.args.getSize(); argIdx++ { | |
| 5348 | 2053 | IVisitable* arg = node.argLst.args.get(argIdx); | |
| 5349 | // Retrieve type of argument | ||
| 5350 | 2053 | const QualType argSymbolType = this.getEvaluatedType(arg); | |
| 5351 | 2053 | const QualType argBaseType = argSymbolType.getBase(); | |
| 5352 | |||
| 5353 | // Re-map some values | ||
| 5354 | 2053 | llvm::Value argVal; | |
| 5355 | 2053 | if argSymbolType.isArray() { | |
| 5356 | // ToDo: Check if GEP can be removed | ||
| 5357 | ✗ | const llvm::Value argValPtr = this.resolveAddress(arg); | |
| 5358 | ✗ | Vector<llvm::Value> indices; | |
| 5359 | ✗ | indices.pushBack(this.getInt64(0l)); | |
| 5360 | ✗ | indices.pushBack(this.getInt32(0l)); | |
| 5361 | ✗ | const llvm::Type argType = argSymbolType.toLLVMType(this.sourceFile); | |
| 5362 | ✗ | argVal = this.insertInBoundsGEP(argType, argValPtr, indices); | |
| 5363 | 2053 | } else if argBaseType.isStringObj() { | |
| 5364 | 108 | llvm::Value argValPtr = this.resolveAddress(arg); | |
| 5365 | 108 | const llvm::Type argBaseLLVMType = argBaseType.toLLVMType(this.sourceFile); | |
| 5366 | 108 | argValPtr = this.insertStructGEP(argBaseLLVMType, argValPtr, 0); | |
| 5367 | 108 | argVal = this.insertLoad(this.builder.getPtrTy(), argValPtr); | |
| 5368 | 108 | } else { | |
| 5369 | 1945 | argVal = this.resolveValue(arg); | |
| 5370 | 2053 | } | |
| 5371 | |||
| 5372 | // Extend all integer types lower than 32 bit to 32 bit | ||
| 5373 | 2053 | const QualType argTypeNoRef = argSymbolType.removeReferenceWrapper(); | |
| 5374 | 2053 | const dyn smallIntTypes = [SuperType::TY_SHORT, SuperType::TY_BYTE, SuperType::TY_CHAR, SuperType::TY_BOOL]; | |
| 5375 | 2053 | if argTypeNoRef.isOneOf(smallIntTypes, len(smallIntTypes)) { | |
| 5376 | 357 | argVal = this.builder.createIntCast(argVal, this.builder.getInt32Ty(), argTypeNoRef.isSigned()); | |
| 5377 | 357 | } | |
| 5378 | |||
| 5379 | 2053 | printfArgs.pushBack(argVal); | |
| 5380 | 2053 | } | |
| 5381 | |||
| 5382 | // Call printf function | ||
| 5383 | 2337 | const llvm::Function printfFct = this.stdFunctionManager.getPrintfFct(); | |
| 5384 | 2337 | llvm::Value callInst = this.builder.createCall(printfFct, printfArgs); | |
| 5385 | |||
| 5386 | // Add noundef attribute to return value and all arguments | ||
| 5387 | 2337 | callInst.addCallRetAttr("noundef"); | |
| 5388 | 6727 | for unsigned int i = 0; i < cast<unsigned int>(printfArgs.getSize()); i++ { | |
| 5389 | 4390 | callInst.addCallParamAttr(i, "noundef"); | |
| 5390 | 4390 | } | |
| 5391 | |||
| 5392 | 4674 | return exprResultAny(exprResultWithValue(callInst)); | |
| 5393 | 2337 | } | |
| 5394 | |||
| 5395 | 99 | f<Any> IRGenerator.visitBuiltinLenCall(ASTFctCallNode* node) { | |
| 5396 | 99 | assert node.fqFunctionName == BUILTIN_FCT_NAME_LEN; | |
| 5397 | |||
| 5398 | // Check if the length is fixed and known via the symbol type | ||
| 5399 | 99 | IVisitable* argNode = node.argLst.args.front(); | |
| 5400 | 99 | QualType symbolType = this.getEvaluatedType(argNode); | |
| 5401 | 99 | symbolType = symbolType.removeReferenceWrapper(); | |
| 5402 | 99 | assert symbolType.is(SuperType::TY_STRING); | |
| 5403 | |||
| 5404 | 99 | const llvm::Function getRawLengthFct = this.stdFunctionManager.getStringGetRawLengthStringFct(); | |
| 5405 | 99 | Vector<llvm::Value> args; | |
| 5406 | 99 | args.pushBack(this.resolveValue(argNode)); | |
| 5407 | 99 | const llvm::Value lengthValue = this.builder.createCall(getRawLengthFct, args); | |
| 5408 | 198 | return exprResultAny(exprResultWithValue(lengthValue)); | |
| 5409 | 99 | } | |
| 5410 | |||
| 5411 | 2933 | f<Any> IRGenerator.visitBuiltinPanicCall(ASTFctCallNode* node) { | |
| 5412 | 2933 | assert node.fqFunctionName == BUILTIN_FCT_NAME_PANIC; | |
| 5413 | |||
| 5414 | 2933 | const llvm::Type ptrTy = this.builder.getPtrTy(); | |
| 5415 | |||
| 5416 | // Get value for stderr | ||
| 5417 | 2933 | const llvm::Value stdErr = this.getStdErrValue(); | |
| 5418 | |||
| 5419 | // Create constant for error message | ||
| 5420 | 2933 | ASTNode* baseNode = cast<ASTNode*>(node); | |
| 5421 | 2933 | const String codeLoc = node.codeLoc.toPrettyString(); | |
| 5422 | 2933 | const String templateStr = "Program panicked at " + codeLoc + ": %s\n" + baseNode.getErrorMessage() + "\n"; | |
| 5423 | 2933 | const String globalName = this.getUnusedGlobalName(String(ANON_GLOBAL_STRING_NAME)); | |
| 5424 | 2933 | llvm::Value globalString = this.module.createGlobalString(templateStr, globalName.getRaw()); | |
| 5425 | |||
| 5426 | // If the output should be comparable, fix alignment to 4 bytes | ||
| 5427 | 2933 | if this.cliOptions.comparableOutput { | |
| 5428 | 2933 | globalString.setAlignment(4); | |
| 5429 | 2933 | } | |
| 5430 | |||
| 5431 | // Get actual error message | ||
| 5432 | 2933 | assert node.hasArgs; | |
| 5433 | 2933 | IVisitable* assignExpr = node.argLst.args.front(); | |
| 5434 | 2933 | const llvm::Value errorObjPtr = this.resolveAddress(assignExpr); | |
| 5435 | 2933 | const QualType errorObjType = this.getEvaluatedType(assignExpr); | |
| 5436 | 2933 | const llvm::Type errorObjTy = errorObjType.toLLVMType(this.sourceFile); | |
| 5437 | 2933 | const llvm::Value errorMessagePtr = this.insertStructGEP(errorObjTy, errorObjPtr, 1); | |
| 5438 | 2933 | const llvm::Value errorMessage = this.insertLoad(ptrTy, errorMessagePtr); | |
| 5439 | |||
| 5440 | // Print the error message to stderr | ||
| 5441 | 2933 | const llvm::Function fprintfFct = this.stdFunctionManager.getFPrintfFct(); | |
| 5442 | 2933 | Vector<llvm::Value> fprintfArgs; | |
| 5443 | 2933 | fprintfArgs.pushBack(stdErr); | |
| 5444 | 2933 | fprintfArgs.pushBack(globalString); | |
| 5445 | 2933 | fprintfArgs.pushBack(errorMessage); | |
| 5446 | 2933 | this.builder.createCall(fprintfFct, fprintfArgs); | |
| 5447 | |||
| 5448 | // Print the recorded error return trace to stderr. A no-op unless error return tracing was enabled somewhere | ||
| 5449 | // along the propagation chain that produced this error - see error_trace_rt.spice. | ||
| 5450 | 2933 | const Vector<llvm::Value> noArgs; | |
| 5451 | 2933 | this.builder.createCall(this.stdFunctionManager.getErrTraceDumpFct(), noArgs); | |
| 5452 | |||
| 5453 | // Print the stack trace of the panic. Emitted ahead of the scope cleanup, which is not part of what led to the panic. | ||
| 5454 | 2933 | if this.cliOptions.printsStackTraceOnAbort() { | |
| 5455 | ✗ | Vector<llvm::Value> dumpArgs; | |
| 5456 | ✗ | dumpArgs.pushBack(this.builder.getTrue()); | |
| 5457 | ✗ | dumpArgs.pushBack(this.builder.getFalse()); | |
| 5458 | ✗ | this.builder.createCall(this.stdFunctionManager.getDumpStacktraceFct(), dumpArgs); | |
| 5459 | ✗ | } | |
| 5460 | |||
| 5461 | // Cleanup the scope before calling exit() | ||
| 5462 | // Unreachable below counts as terminator | ||
| 5463 | 2933 | this.terminateBlock(baseNode.getNextOuterStmtLst()); | |
| 5464 | |||
| 5465 | // Generate call to exit(). The cleanup code is attributed to the closing brace of the scope, the exit to the panic call | ||
| 5466 | 2933 | this.diGenerator.setSourceLocation(baseNode); | |
| 5467 | 2933 | const llvm::Function exitFct = this.stdFunctionManager.getExitFct(); | |
| 5468 | 2933 | Vector<llvm::Value> exitArgs; | |
| 5469 | 2933 | exitArgs.pushBack(this.getInt32(cast<long>(EXIT_FAILURE))); | |
| 5470 | 2933 | this.builder.createCall(exitFct, exitArgs); | |
| 5471 | // Create unreachable instruction | ||
| 5472 | 2933 | this.builder.createUnreachable(); | |
| 5473 | |||
| 5474 | 5866 | return Any(); | |
| 5475 | 2933 | } | |
| 5476 | |||
| 5477 | 5 | f<Any> IRGenerator.visitBuiltinSyscallCall(ASTFctCallNode* node) { | |
| 5478 | 5 | assert node.fqFunctionName == BUILTIN_FCT_NAME_SYSCALL; | |
| 5479 | |||
| 5480 | // Determine the required number of operands. | ||
| 5481 | // (We assume at least one argument is provided: the syscall number) | ||
| 5482 | 5 | assert node.hasArgs; | |
| 5483 | 5 | const unsigned int requiredRegs = cast<unsigned int>(node.argLst.args.getSize()); | |
| 5484 | 5 | assert requiredRegs >= 1 && requiredRegs <= 7; | |
| 5485 | |||
| 5486 | // Create the asm and constraint strings based on the required number of registers. | ||
| 5487 | 5 | const String asmString = this.getSysCallAsmString(requiredRegs); | |
| 5488 | 5 | const String constraints = this.getSysCallConstraintString(requiredRegs); | |
| 5489 | |||
| 5490 | // Create the LLVM function type for the inline asm with only the needed operands. | ||
| 5491 | // On i386, the registers are only 32 bits wide and with six args the syscall number is passed via memory. | ||
| 5492 | 5 | const bool isX86 = this.cliOptions.targetTriple.isX86(); | |
| 5493 | 5 | const bool passSysCallNumberViaMemory = isX86 && requiredRegs == 7; | |
| 5494 | 5 | const llvm::Type int64Ty = this.builder.getInt64Ty(); | |
| 5495 | 5 | const llvm::Type regTy = isX86 ? this.builder.getInt32Ty() : int64Ty; | |
| 5496 | 5 | Vector<llvm::Type> argTypes; | |
| 5497 | 27 | for unsigned int i = 0; i < requiredRegs; i++ { | |
| 5498 | 22 | argTypes.pushBack(i == 0 && passSysCallNumberViaMemory ? this.builder.getPtrTy() : regTy); | |
| 5499 | 22 | } | |
| 5500 | 5 | const llvm::Type fctType = llvm::getFunctionType(regTy, argTypes, false); | |
| 5501 | 5 | const llvm::Value inlineAsm = llvm::getInlineAsm(fctType, asmString, constraints, true); | |
| 5502 | |||
| 5503 | // Build the argument list (each provided argument is converted to the register type). | ||
| 5504 | 5 | Vector<llvm::Value> argValues; | |
| 5505 | 5 | argValues.reserve(requiredRegs); | |
| 5506 | 27 | for unsigned int i = 0; i < requiredRegs; i++ { | |
| 5507 | 22 | IVisitable* argNode = node.argLst.args.get(i); | |
| 5508 | 22 | const QualType argType = this.getEvaluatedType(argNode); | |
| 5509 | 22 | if argType.is(SuperType::TY_PTR) || argType.is(SuperType::TY_STRING) { | |
| 5510 | 3 | argValues.pushBack(this.builder.createPtrToInt(this.resolveValue(argNode), regTy)); | |
| 5511 | 3 | } else { | |
| 5512 | 19 | argValues.pushBack(this.builder.createZExtOrTrunc(this.resolveValue(argNode), regTy)); | |
| 5513 | 19 | } | |
| 5514 | 22 | } | |
| 5515 | 5 | if passSysCallNumberViaMemory { | |
| 5516 | ✗ | const llvm::Value sysCallNumberPtr = this.insertAlloca(regTy, String("syscall.number")); | |
| 5517 | ✗ | this.insertStore(argValues.front(), sysCallNumberPtr); | |
| 5518 | ✗ | llvm::Value& firstArg = argValues.front(); | |
| 5519 | ✗ | firstArg = sysCallNumberPtr; | |
| 5520 | ✗ | } | |
| 5521 | |||
| 5522 | // Generate the call using only the required number of arguments. | ||
| 5523 | 5 | llvm::Value call = this.builder.createCall(fctType, inlineAsm, argValues); | |
| 5524 | 5 | if passSysCallNumberViaMemory { | |
| 5525 | ✗ | call.addCallParamTypeAttr(0, "elementtype", regTy); | |
| 5526 | ✗ | } | |
| 5527 | 5 | const llvm::Value res = this.builder.createSExtOrTrunc(call, int64Ty); | |
| 5528 | |||
| 5529 | 10 | return exprResultAny(exprResultWithValue(res)); | |
| 5530 | 5 | } | |
| 5531 | |||
| 5532 | 238 | f<Any> IRGenerator.visitBuiltinNewCall(ASTFctCallNode* node) { | |
| 5533 | 238 | assert node.fqFunctionName == BUILTIN_FCT_NAME_NEW; | |
| 5534 | |||
| 5535 | 238 | const FctCallData& data = node.data.get(this.manIdx); | |
| 5536 | 238 | const QualType& templateType = data.templateTypes.front(); | |
| 5537 | |||
| 5538 | // Allocate sizeof(T) bytes on the heap | ||
| 5539 | 238 | const llvm::Type llvmType = templateType.toLLVMType(this.sourceFile); | |
| 5540 | 238 | const unsigned long typeSize = this.module.getTypeAllocSize(llvmType); | |
| 5541 | 238 | const llvm::Function allocFct = this.stdFunctionManager.getAllocUnsafeLongFct(); | |
| 5542 | 238 | Vector<llvm::Value> allocArgs; | |
| 5543 | 238 | allocArgs.pushBack(this.builder.getInt64(typeSize)); | |
| 5544 | 238 | const llvm::Value targetPtr = this.builder.createCall(allocFct, allocArgs); | |
| 5545 | |||
| 5546 | // Call the constructor if one was resolved during type checking | ||
| 5547 | 238 | const model::Function* ctor = data.callee; | |
| 5548 | 238 | if ctor != nil<const model::Function*> { | |
| 5549 | 228 | Vector<llvm::Value> ctorArgValues; | |
| 5550 | 228 | if node.hasArgs { | |
| 5551 | 214 | const QualTypeList paramTypes = ctor.getParamTypes(); | |
| 5552 | 446 | for unsigned long i = 0l; i < node.argLst.args.getSize(); i++ { | |
| 5553 | 232 | IVisitable* argNode = node.argLst.args.get(i); | |
| 5554 | 232 | const QualType& paramType = paramTypes.get(i); | |
| 5555 | 232 | if paramType.isRef() { | |
| 5556 | 41 | ctorArgValues.pushBack(this.resolveAddress(argNode)); | |
| 5557 | 232 | } else if paramType.isOwnedByValueParam(cast<ASTNode*>(node)) { | |
| 5558 | 1 | ctorArgValues.pushBack(this.generateOwnedArgValue(this.resolveAddress(argNode), paramType)); | |
| 5559 | 1 | } else { | |
| 5560 | 190 | ctorArgValues.pushBack(this.resolveValue(argNode)); | |
| 5561 | 191 | } | |
| 5562 | 232 | } | |
| 5563 | 214 | } | |
| 5564 | 228 | this.generateCtorOrDtorCall(targetPtr, ctor, ctorArgValues); | |
| 5565 | 228 | } else { | |
| 5566 | 10 | llvm::Value initVal; | |
| 5567 | 10 | if node.hasArgs { | |
| 5568 | 5 | initVal = this.resolveValue(node.argLst.args.front()); | |
| 5569 | 5 | } else { | |
| 5570 | 5 | initVal = this.getDefaultValueForSymbolType(templateType); | |
| 5571 | 5 | } | |
| 5572 | 10 | this.insertStore(initVal, targetPtr); | |
| 5573 | 10 | } | |
| 5574 | |||
| 5575 | 476 | return exprResultAny(exprResultWithValue(targetPtr)); | |
| 5576 | 238 | } | |
| 5577 | |||
| 5578 | 522 | f<Any> IRGenerator.visitBuiltinPlacementNewCall(ASTFctCallNode* node) { | |
| 5579 | 522 | assert node.fqFunctionName == BUILTIN_FCT_NAME_PLACEMENT_NEW; | |
| 5580 | |||
| 5581 | 522 | const FctCallData& data = node.data.get(this.manIdx); | |
| 5582 | |||
| 5583 | // Resolve the target address from the first argument (the byte* pointer) | ||
| 5584 | 522 | const llvm::Value targetPtr = this.resolveValue(node.argLst.args.front()); | |
| 5585 | |||
| 5586 | // Call the constructor if one was resolved during type checking | ||
| 5587 | 522 | const model::Function* ctor = data.callee; | |
| 5588 | 522 | if ctor != nil<const model::Function*> { | |
| 5589 | 277 | Vector<llvm::Value> ctorArgValues; | |
| 5590 | 277 | const QualTypeList paramTypes = ctor.getParamTypes(); | |
| 5591 | 557 | for unsigned long i = 1l; i < node.argLst.args.getSize(); i++ { | |
| 5592 | 280 | IVisitable* argNode = node.argLst.args.get(i); | |
| 5593 | 280 | const QualType& paramType = paramTypes.get(i - 1l); | |
| 5594 | 280 | if paramType.isRef() { | |
| 5595 | 274 | ctorArgValues.pushBack(this.resolveAddress(argNode)); | |
| 5596 | 280 | } else if paramType.isOwnedByValueParam(cast<ASTNode*>(node)) { | |
| 5597 | ✗ | ctorArgValues.pushBack(this.generateOwnedArgValue(this.resolveAddress(argNode), paramType)); | |
| 5598 | ✗ | } else { | |
| 5599 | 6 | ctorArgValues.pushBack(this.resolveValue(argNode)); | |
| 5600 | 6 | } | |
| 5601 | 280 | } | |
| 5602 | 277 | this.generateCtorOrDtorCall(targetPtr, ctor, ctorArgValues); | |
| 5603 | 277 | } else { | |
| 5604 | 245 | const QualType& templateType = data.templateTypes.front(); | |
| 5605 | 245 | const Vector<IVisitable*>& args = node.argLst.args; | |
| 5606 | 245 | llvm::Value initVal; | |
| 5607 | 245 | if args.getSize() > 1l { | |
| 5608 | 245 | initVal = this.resolveValue(args.get(1l)); | |
| 5609 | 245 | } else { | |
| 5610 | ✗ | initVal = this.getDefaultValueForSymbolType(templateType); | |
| 5611 | ✗ | } | |
| 5612 | 245 | this.insertStore(initVal, targetPtr); | |
| 5613 | 245 | } | |
| 5614 | |||
| 5615 | 522 | return exprResultAny(exprResultWithValue(targetPtr)); | |
| 5616 | } | ||
| 5617 | |||
| 5618 | 924 | f<Any> IRGenerator.visitBuiltinErrTraceBufferCall(ASTFctCallNode* node) { | |
| 5619 | 924 | assert node.fqFunctionName == BUILTIN_FCT_NAME_ERR_TRACE_BUFFER; | |
| 5620 | |||
| 5621 | // Get or create the thread-local ErrorTraceBuffer instance for this module. It is only ever referenced from | ||
| 5622 | // within error_trace_rt.spice's own wrapper procedures (all compiled into this same module), so internal | ||
| 5623 | // linkage is enough - no other module ever needs to see this symbol. | ||
| 5624 | 924 | const string tlsGlobalName = "__spice_errTraceBufferTLS"; | |
| 5625 | 924 | llvm::Value tlsBuffer = this.module.getNamedGlobal(tlsGlobalName); | |
| 5626 | 924 | if tlsBuffer.isNull() { | |
| 5627 | 308 | const QualType evaluatedType = this.getEvaluatedType(cast<IVisitable*>(node)); | |
| 5628 | 308 | const QualType bufferSType = evaluatedType.removeReferenceWrapper(); | |
| 5629 | 308 | const llvm::Type bufferType = bufferSType.toLLVMType(this.sourceFile); | |
| 5630 | 308 | const llvm::Value zeroInit = llvm::getNullValue(bufferType); | |
| 5631 | 308 | tlsBuffer = this.module.addGlobal(bufferType, tlsGlobalName); | |
| 5632 | 308 | tlsBuffer.setConstant(false); | |
| 5633 | 308 | tlsBuffer.setLinkage(llvm::LLVMLinkage::InternalLinkage); | |
| 5634 | 308 | tlsBuffer.setInitializer(zeroInit); | |
| 5635 | 308 | tlsBuffer.setThreadLocal(true); | |
| 5636 | 308 | } | |
| 5637 | |||
| 5638 | 924 | return exprResultAny(exprResultWithPtr(tlsBuffer)); | |
| 5639 | } | ||
| 5640 | |||
| 5641 | 952 | f<Any> IRGenerator.visitBuiltinStdErrCall(ASTFctCallNode* node) { | |
| 5642 | 952 | assert node.fqFunctionName == BUILTIN_FCT_NAME_STDERR; | |
| 5643 | |||
| 5644 | 952 | return exprResultAny(exprResultWithValue(this.getStdErrValue())); | |
| 5645 | } | ||
| 5646 | |||
| 5647 | /** | ||
| 5648 | * Emits a call to LLVM's target-independent 'llvm.frameaddress' intrinsic at level 0, yielding the frame address | ||
| 5649 | * (saved frame-pointer slot) of whichever Spice function this builtin call is inlined into - the same value as | ||
| 5650 | * C's `__builtin_frame_address(0)`. | ||
| 5651 | * | ||
| 5652 | * Frame pointers are kept for every Spice function we emit (see 'frame-pointer' in IRGenerator::addCommonFctAttrs), | ||
| 5653 | * so the returned address is a real frame address. That does NOT make a frame-pointer chain walkable in general: | ||
| 5654 | * the layout of the slots around it is target- and ABI-specific, and on Windows x64 in particular there is no | ||
| 5655 | * saved-frame-pointer chain at all, since the frame pointer points into the middle of the frame and unwinding goes | ||
| 5656 | * through the .pdata/.xdata tables instead. Anything that needs an actual call stack must go through the platform | ||
| 5657 | * unwinder rather than walking from this value. | ||
| 5658 | */ | ||
| 5659 | ✗ | f<Any> IRGenerator.visitBuiltinFrameAddressCall(ASTFctCallNode* node) { | |
| 5660 | ✗ | assert node.fqFunctionName == BUILTIN_FCT_NAME_FRAME_ADDRESS; | |
| 5661 | |||
| 5662 | ✗ | Vector<llvm::Type> overloadTypes; | |
| 5663 | ✗ | overloadTypes.pushBack(this.builder.getPtrTy()); | |
| 5664 | ✗ | const llvm::Function frameAddressFct = this.module.getIntrinsicDeclaration("llvm.frameaddress", overloadTypes); | |
| 5665 | // Level 0 = this frame; LLVM does not guarantee levels > 0 work on every target. | ||
| 5666 | ✗ | Vector<llvm::Value> args; | |
| 5667 | ✗ | args.pushBack(this.getInt32(0l)); | |
| 5668 | ✗ | const llvm::Value frameAddress = this.builder.createCall(frameAddressFct, args); | |
| 5669 | |||
| 5670 | ✗ | return exprResultAny(exprResultWithValue(frameAddress)); | |
| 5671 | ✗ | } | |
| 5672 | |||
| 5673 | /** | ||
| 5674 | * Retrieves the process's stderr stream (FILE*). Spice has no syntax for declaring an external variable, so this | ||
| 5675 | * reaches the real libc symbol directly, the same way on every platform: an ACRT accessor call on Windows, and | ||
| 5676 | * the platform-specific extern global everywhere else. | ||
| 5677 | */ | ||
| 5678 | 8707 | f<llvm::Value> IRGenerator.getStdErrValue() { | |
| 5679 | 8707 | const llvm::Type ptrTy = this.builder.getPtrTy(); | |
| 5680 | |||
| 5681 | 8707 | if this.cliOptions.targetTriple.isOSWindows() { | |
| 5682 | 58 | const llvm::Function getAcrtIOFuncFct = this.stdFunctionManager.getAcrtIOFuncFct(); | |
| 5683 | 58 | Vector<llvm::Value> args; | |
| 5684 | 58 | args.pushBack(this.getInt32(2l)); // Constant for stderr | |
| 5685 | 116 | return this.builder.createCall(getAcrtIOFuncFct, args); | |
| 5686 | 58 | } | |
| 5687 | |||
| 5688 | 8649 | const string globalName = this.cliOptions.targetTriple.isOSDarwin() ? "__stderrp" : "stderr"; | |
| 5689 | 8649 | llvm::Value stdErrPtr = this.module.getNamedGlobal(globalName); | |
| 5690 | 8649 | if stdErrPtr.isNull() { | |
| 5691 | 1678 | stdErrPtr = this.module.getOrInsertGlobal(globalName, ptrTy); | |
| 5692 | 1678 | stdErrPtr.setLinkage(llvm::LLVMLinkage::ExternalLinkage); | |
| 5693 | 1678 | stdErrPtr.setUnnamedAddr(llvm::LLVMUnnamedAddr::LocalUnnamedAddr); | |
| 5694 | 1678 | stdErrPtr.setAlignment(8); | |
| 5695 | 1678 | } | |
| 5696 | 8649 | return this.insertLoad(ptrTy, stdErrPtr); | |
| 5697 | } | ||
| 5698 | |||
| 5699 | // ===================================================== Implicit functions ====================================================== | ||
| 5700 | |||
| 5701 | 75 | f<llvm::Value> IRGenerator.doImplicitCast(llvm::Value src, const QualType& origDstSTy, const QualType& origSrcSTy) { | |
| 5702 | 75 | assert !origSrcSTy.matches(origDstSTy, false, false, false); // We only need to cast implicitly, if the types do not match exactly | |
| 5703 | 75 | QualType dstSTy = origDstSTy; | |
| 5704 | 75 | QualType srcSTy = origSrcSTy; | |
| 5705 | |||
| 5706 | // Unpack the pointers until a pointer of another type is met | ||
| 5707 | 75 | unsigned long loadCounter = 0l; | |
| 5708 | 75 | while srcSTy.isPtr() { | |
| 5709 | ✗ | src = this.insertLoad(srcSTy, src); | |
| 5710 | ✗ | srcSTy = srcSTy.getContained(); | |
| 5711 | ✗ | dstSTy = dstSTy.getContained(); | |
| 5712 | ✗ | loadCounter++; | |
| 5713 | ✗ | } | |
| 5714 | // GEP or bit-cast | ||
| 5715 | 75 | if dstSTy.isArray() && srcSTy.isArray() { // Special case that is used for passing arrays as pointer to functions | |
| 5716 | 75 | Vector<llvm::Value> indices; | |
| 5717 | 75 | indices.pushBack(this.getInt64(0l)); | |
| 5718 | 75 | indices.pushBack(this.getInt32(0l)); | |
| 5719 | 75 | src = this.insertInBoundsGEP(srcSTy.toLLVMType(this.sourceFile), src, indices); | |
| 5720 | 75 | } else { | |
| 5721 | ✗ | src = this.insertLoad(srcSTy, src); | |
| 5722 | ✗ | src = this.builder.createBitCast(src, dstSTy.toLLVMType(this.sourceFile)); | |
| 5723 | ✗ | } | |
| 5724 | // Pack the pointers together again | ||
| 5725 | 75 | while loadCounter > 0l { | |
| 5726 | ✗ | const llvm::Value newActualArg = this.insertAlloca(srcSTy); | |
| 5727 | ✗ | this.insertStore(src, newActualArg); | |
| 5728 | ✗ | src = newActualArg; | |
| 5729 | ✗ | loadCounter--; | |
| 5730 | ✗ | } | |
| 5731 | 75 | return src; | |
| 5732 | } | ||
| 5733 | |||
| 5734 | 15 | public f<llvm::Value> IRGenerator.getUpcastedStructPtr(llvm::Value structPtr, const QualType& dstType, const QualType& srcType) { | |
| 5735 | // Adjusts a pointer to a struct instance so it points at the embedded subobject the destination type | ||
| 5736 | // refers to. This backs the explicit 'cast<Base*>(derived)' / 'cast<Interface*>(struct)' conversions. | ||
| 5737 | // When the struct carries a vtable prefix (because it implements interfaces), the composed base no longer | ||
| 5738 | // sits at offset 0, so the pointer has to be advanced via a GEP. For subobjects that already sit at offset | ||
| 5739 | // 0 (e.g. the leading interface field), insertStructGEP() with index 0 is a no-op and returns the pointer | ||
| 5740 | // unchanged, so no superfluous IR is emitted. If the given pair of types is not such an upcast, the | ||
| 5741 | // pointer is returned unchanged. | ||
| 5742 | 15 | QualType dstPointee = dstType.removeReferenceWrapper(); | |
| 5743 | 15 | QualType srcPointee = srcType.removeReferenceWrapper(); | |
| 5744 | // Both sides are pointers ('structPtr' is the pointer value itself), so look one level deeper | ||
| 5745 | 15 | if dstPointee.isPtr() && srcPointee.isPtr() { | |
| 5746 | 6 | dstPointee = dstPointee.getContained(); | |
| 5747 | 6 | srcPointee = srcPointee.getContained(); | |
| 5748 | 6 | } | |
| 5749 | // Bail out if this is not an interface/composed-base upcast that requires a pointer adjustment | ||
| 5750 | 15 | if !srcPointee.is(SuperType::TY_STRUCT) { | |
| 5751 | ✗ | return structPtr; | |
| 5752 | } | ||
| 5753 | 15 | if !dstPointee.matchesInterfaceImplementedByStruct(srcPointee) && !dstPointee.matchesComposedBaseOfStruct(srcPointee) { | |
| 5754 | ✗ | return structPtr; | |
| 5755 | } | ||
| 5756 | |||
| 5757 | 15 | QualType walkType = srcPointee; | |
| 5758 | 21 | while !walkType.matches(dstPointee, false, true, true) { | |
| 5759 | 17 | assert walkType.is(SuperType::TY_STRUCT); | |
| 5760 | 17 | Scope* structScope = walkType.getBodyScope(); | |
| 5761 | 17 | assert structScope != nil<Scope*>; | |
| 5762 | 17 | const Struct* spiceStruct = walkType.getStruct(nil<ASTNode*>); | |
| 5763 | 17 | assert spiceStruct != nil<const Struct*>; | |
| 5764 | 17 | const llvm::Type llvmStructType = walkType.toLLVMType(this.sourceFile); | |
| 5765 | // Implicit (interface) fields are inserted before the explicit fields, so their count is the difference | ||
| 5766 | // between the total field count and the number of explicit fields. | ||
| 5767 | 17 | const unsigned long implicitFieldCount = structScope.getFieldCount() - spiceStruct.fieldTypes.getSize(); | |
| 5768 | |||
| 5769 | // Case 1: the destination is an interface implemented directly by the struct -> step into its implicit field | ||
| 5770 | 17 | if dstPointee.is(SuperType::TY_INTERFACE) { | |
| 5771 | 11 | bool found = false; | |
| 5772 | 11 | for unsigned long i = 0l; i < implicitFieldCount; i++ { | |
| 5773 | 11 | const SymbolTableEntry* implicitField = structScope.lookupField(cast<unsigned int>(i)); | |
| 5774 | 11 | if dstPointee.matches(implicitField.getQualType(), false, true, true) { | |
| 5775 | 11 | structPtr = this.insertStructGEP(llvmStructType, structPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(implicitField.orderIndex))); | |
| 5776 | 11 | found = true; | |
| 5777 | 11 | break; | |
| 5778 | } | ||
| 5779 | ✗ | } | |
| 5780 | 11 | assert found; | |
| 5781 | 11 | return structPtr; | |
| 5782 | } | ||
| 5783 | |||
| 5784 | // Case 2: the destination is a composed base struct -> step into the first explicit (composed) field | ||
| 5785 | 6 | const SymbolTableEntry* baseField = structScope.lookupField(cast<unsigned int>(implicitFieldCount)); | |
| 5786 | 6 | assert baseField != nil<const SymbolTableEntry*>; | |
| 5787 | 6 | const QualType& baseFieldType = baseField.getQualType(); | |
| 5788 | 6 | assert baseFieldType.isComposition(); | |
| 5789 | 6 | structPtr = this.insertStructGEP(llvmStructType, structPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(baseField.orderIndex))); | |
| 5790 | 6 | walkType = baseFieldType; | |
| 5791 | 6 | } | |
| 5792 | 4 | return structPtr; | |
| 5793 | } | ||
| 5794 | |||
| 5795 | /** | ||
| 5796 | * Generate cleanup code (dtor calls, deallocations, lifetime end markers) for the scope of the given statement list, which is | ||
| 5797 | * the current scope | ||
| 5798 | * | ||
| 5799 | * @param node Statement list of the scope | ||
| 5800 | * @param returnedLocal Local variable, that is handed over to the caller by a return statement. It must not be destructed. | ||
| 5801 | * Nil if there is none. | ||
| 5802 | */ | ||
| 5803 | 93726 | p IRGenerator.generateScopeCleanup(ASTStmtLstNode* node, SymbolTableEntry* returnedLocal = nil<SymbolTableEntry*>) { | |
| 5804 | 93726 | this.generateScopeCleanup(node.resourcesToCleanup.get(this.manIdx), this.currentScope, node.closingBraceCodeLoc, returnedLocal); | |
| 5805 | 93726 | } | |
| 5806 | |||
| 5807 | /** | ||
| 5808 | * Generate cleanup code (dtor calls, deallocations, lifetime end markers) for the given scope | ||
| 5809 | * | ||
| 5810 | * @param resources Resources to clean up, as determined by the type checker | ||
| 5811 | * @param scope Scope to clean up | ||
| 5812 | * @param codeLoc Code location to attribute the cleanup code to | ||
| 5813 | * @param returnedLocal Local variable, that is handed over to the caller by a return statement. It must not be destructed. | ||
| 5814 | * Nil if there is none. | ||
| 5815 | */ | ||
| 5816 | 171434 | p IRGenerator.generateScopeCleanup(const ResourcesForManifestationToCleanup& resources, Scope* scope, const CodeLoc& codeLoc, SymbolTableEntry* returnedLocal = nil<SymbolTableEntry*>) { | |
| 5817 | 171434 | this.diGenerator.setSourceLocation(codeLoc); | |
| 5818 | |||
| 5819 | // Do not clean up if the block is already terminated | ||
| 5820 | 171434 | if this.blockAlreadyTerminated { | |
| 5821 | 48556 | return; | |
| 5822 | } | ||
| 5823 | |||
| 5824 | // Call all dtor functions | ||
| 5825 | 122878 | const Vector<llvm::Value> noArgs; | |
| 5826 | 130137 | foreach const Pair<SymbolTableEntry*, model::Function*>& dtorToCall : resources.dtorFunctionsToCall { | |
| 5827 | 7259 | if dtorToCall.getFirst() != returnedLocal { | |
| 5828 | 7082 | this.generateCtorOrDtorCall(dtorToCall.getFirst(), dtorToCall.getSecond(), noArgs); | |
| 5829 | 7082 | } | |
| 5830 | 7259 | } | |
| 5831 | |||
| 5832 | // Deallocate all heap variables that go out of scope and are currently owned | ||
| 5833 | 123368 | foreach const SymbolTableEntry* entry : resources.heapVarsToFree { | |
| 5834 | 490 | this.generateDeallocCall(this.getAddress(entry)); | |
| 5835 | 490 | } | |
| 5836 | |||
| 5837 | // Generate lifetime end markers | ||
| 5838 | 122878 | if this.cliOptions.useLifetimeMarkers { | |
| 5839 | 4087 | const Vector<SymbolTableEntry*> varsGoingOutOfScope = scope.getVarsGoingOutOfScope(); | |
| 5840 | 5977 | foreach const SymbolTableEntry* var : varsGoingOutOfScope { | |
| 5841 | 1890 | const llvm::Value address = this.getAddress(var); | |
| 5842 | // Only allocas get a lifetime start marker and llvm.lifetime.end rejects anything else. This excludes e.g. the phi | ||
| 5843 | // of a ternary with a reference result, which refers to the storage of one of its operands. | ||
| 5844 | 1890 | if !address.isNull() && address.isAllocaInst() { | |
| 5845 | 1458 | this.createLifetimeMarker("llvm.lifetime.end", address); | |
| 5846 | 1458 | } | |
| 5847 | 1890 | } | |
| 5848 | 4087 | } | |
| 5849 | 122878 | } | |
| 5850 | |||
| 5851 | /** | ||
| 5852 | * Generate cleanup code for the head scope of a for or foreach loop, which holds the variables declared in the head of the | ||
| 5853 | * loop. Those live for the whole loop, so this is generated where the loop is left, not at the end of each round. | ||
| 5854 | * | ||
| 5855 | * @param loopNode For or foreach loop node | ||
| 5856 | * @param headScope Head scope of the loop | ||
| 5857 | * @param returnedLocal Local variable, that is handed over to the caller by a return statement. It must not be destructed. | ||
| 5858 | * Nil if there is none. | ||
| 5859 | */ | ||
| 5860 | 9095 | p IRGenerator.generateLoopHeadCleanup(ASTNode* loopNode, Scope* headScope, SymbolTableEntry* returnedLocal = nil<SymbolTableEntry*>) { | |
| 5861 | 9095 | assert headScope.scopeType == ScopeType::LOOP_HEAD; | |
| 5862 | 9095 | if loopNode.kind == ASTNodeKind::FOR_LOOP { | |
| 5863 | 8317 | const ASTForLoopNode* forLoopNode = downcast<ASTForLoopNode>(loopNode); | |
| 5864 | 8317 | const ASTStmtLstNode* body = downcast<ASTStmtLstNode>(getASTNode(forLoopNode.body)); | |
| 5865 | 8317 | const ResourcesForManifestationToCleanup& resources = forLoopNode.headResourcesToCleanup.get(this.manIdx); | |
| 5866 | 8317 | this.generateScopeCleanup(resources, headScope, body.closingBraceCodeLoc, returnedLocal); | |
| 5867 | 8317 | } else { | |
| 5868 | 778 | assert loopNode.kind == ASTNodeKind::FOREACH_LOOP; | |
| 5869 | 778 | const ASTForeachLoopNode* foreachLoopNode = downcast<ASTForeachLoopNode>(loopNode); | |
| 5870 | 778 | const ASTStmtLstNode* body = downcast<ASTStmtLstNode>(getASTNode(foreachLoopNode.body)); | |
| 5871 | 778 | const ResourcesForManifestationToCleanup& resources = foreachLoopNode.headResourcesToCleanup.get(this.manIdx); | |
| 5872 | 778 | this.generateScopeCleanup(resources, headScope, body.closingBraceCodeLoc, returnedLocal); | |
| 5873 | 778 | } | |
| 5874 | 9095 | } | |
| 5875 | |||
| 5876 | /** | ||
| 5877 | * Generate the dtor calls for the temporaries of an expression scope. Those were determined by the type checker. | ||
| 5878 | * | ||
| 5879 | * @param exprScope Expression scope to generate the dtor calls for | ||
| 5880 | * @param node Expression the scope belongs to | ||
| 5881 | */ | ||
| 5882 | 44561 | p IRGenerator.generateTemporariesCleanup(const Scope* exprScope, ASTNode* node) { | |
| 5883 | 44561 | if exprScope.temporaryDtorsToCall.isEmpty() { | |
| 5884 | 44527 | return; | |
| 5885 | } | ||
| 5886 | 34 | this.diGenerator.setSourceLocation(node); | |
| 5887 | 34 | const Vector<llvm::Value> noArgs; | |
| 5888 | 69 | foreach const Pair<SymbolTableEntry*, model::Function*>& dtorToCall : exprScope.temporaryDtorsToCall { | |
| 5889 | 35 | this.generateCtorOrDtorCall(dtorToCall.getFirst(), dtorToCall.getSecond(), noArgs); | |
| 5890 | 35 | } | |
| 5891 | 34 | } | |
| 5892 | |||
| 5893 | /** | ||
| 5894 | * Get the if, while, do-while, for or foreach statement, if the given node is part of its header, e.g. of its condition. | ||
| 5895 | * | ||
| 5896 | * The IR for a header is generated within the scope of its statement, but the header is not part of the body of the statement. | ||
| 5897 | * Hence, the nearest statement list of a node in the header is the one around the statement, and not the one of that scope. | ||
| 5898 | * | ||
| 5899 | * @param node Node to check | ||
| 5900 | * @return Statement, whose header the node is part of. Nil if the node is not part of a header | ||
| 5901 | */ | ||
| 5902 | 43048 | f<ASTNode*> getStatementOfHeader(const ASTNode* node) { | |
| 5903 | 43048 | ASTNode* ancestor = node.parent; | |
| 5904 | 43163 | while ancestor != nil<ASTNode*> { | |
| 5905 | 43163 | if ancestor.kind == ASTNodeKind::STMT_LST { | |
| 5906 | 43041 | return nil<ASTNode*>; // The node is part of a body | |
| 5907 | } | ||
| 5908 | 122 | const ASTNodeKind kind = ancestor.kind; | |
| 5909 | 122 | if kind == ASTNodeKind::IF_STMT || kind == ASTNodeKind::WHILE_LOOP || kind == ASTNodeKind::DO_WHILE_LOOP || kind == ASTNodeKind::FOR_LOOP || kind == ASTNodeKind::FOREACH_LOOP { | |
| 5910 | 7 | return ancestor; | |
| 5911 | } | ||
| 5912 | 115 | ancestor = ancestor.parent; | |
| 5913 | 115 | } | |
| 5914 | ✗ | return nil<ASTNode*>; | |
| 5915 | } | ||
| 5916 | |||
| 5917 | /** | ||
| 5918 | * Generate cleanup code (dtor calls, deallocations) for every scope between the given node (exclusive) and the given | ||
| 5919 | * target scope (inclusive). This is required for jumps that leave more than one scope at once (e.g. break/continue/return), | ||
| 5920 | * since those skip the normal fall-through cleanup that visitStmtLst() generates for each of the enclosing scopes. | ||
| 5921 | * | ||
| 5922 | * The jump can also originate from the middle of an expression (the error propagation operator), which is evaluated in scopes | ||
| 5923 | * that have no statement list to pair with the scope. These are handled first, so that the scopes and statement lists match up | ||
| 5924 | * for the rest of the way. | ||
| 5925 | * | ||
| 5926 | * @param node Node the jump originates from | ||
| 5927 | * @param targetScope Outermost scope that is left by the jump; cleanup is generated for this scope as well | ||
| 5928 | * @param returnedLocal Local variable, that is handed over to the caller by the jump (a return statement). It must not be | ||
| 5929 | * destructed. Nil if there is none. | ||
| 5930 | */ | ||
| 5931 | 43048 | p IRGenerator.generateScopeCleanupUpTo(ASTNode* node, const Scope* targetScope, SymbolTableEntry* returnedLocal = nil<SymbolTableEntry*>) { | |
| 5932 | 43048 | assert targetScope != nil<const Scope*>; | |
| 5933 | 43048 | Scope* scopeLevel = this.currentScope; | |
| 5934 | |||
| 5935 | // Leave the expression scopes, which hold the temporaries of the expression that is evaluated. Only the ones that were already | ||
| 5936 | // constructed are destructed here, since the others do not have an address yet | ||
| 5937 | 43063 | while scopeLevel.scopeType == ScopeType::EXPR_BODY { | |
| 5938 | 15 | this.generateTemporariesCleanup(scopeLevel, node); | |
| 5939 | 15 | scopeLevel = scopeLevel.parent; | |
| 5940 | 15 | } | |
| 5941 | |||
| 5942 | // Leave the scope of the statement, if the jump originates from its header. The body was not entered yet, or it was already | ||
| 5943 | // left, so there is nothing of it to clean up. The head of a for or foreach loop has a scope of its own though, which holds | ||
| 5944 | // the variables the head declares. Those are alive while the head is evaluated. | ||
| 5945 | 43048 | ASTNode* headerStatement = getStatementOfHeader(node); | |
| 5946 | 43048 | if scopeLevel != targetScope && headerStatement != nil<ASTNode*> { | |
| 5947 | 7 | if scopeLevel.scopeType == ScopeType::LOOP_HEAD { | |
| 5948 | 2 | this.generateLoopHeadCleanup(headerStatement, scopeLevel, returnedLocal); | |
| 5949 | 2 | } | |
| 5950 | 7 | scopeLevel = scopeLevel.parent; | |
| 5951 | 7 | } | |
| 5952 | |||
| 5953 | 43048 | ASTStmtLstNode* scope = node.getNextOuterStmtLst(); | |
| 5954 | 68613 | while true { | |
| 5955 | 68613 | this.generateScopeCleanup(scope.resourcesToCleanup.get(this.manIdx), scopeLevel, scope.closingBraceCodeLoc, returnedLocal); | |
| 5956 | 68613 | if scopeLevel == targetScope { | |
| 5957 | 43048 | break; | |
| 5958 | } | ||
| 5959 | 25565 | assert scope.parent != nil<ASTNode*> && scopeLevel.parent != nil<Scope*>; | |
| 5960 | 25565 | scopeLevel = scopeLevel.parent; | |
| 5961 | // The head of a for or foreach loop is a scope of its own, which has no statement list. The loop owns the statement list | ||
| 5962 | // of its body, which was cleaned up just now | ||
| 5963 | 25565 | if scopeLevel.scopeType == ScopeType::LOOP_HEAD { | |
| 5964 | 3631 | this.generateLoopHeadCleanup(scope.parent, scopeLevel, returnedLocal); | |
| 5965 | 3631 | assert scopeLevel != targetScope && scopeLevel.parent != nil<Scope*>; | |
| 5966 | 3631 | scopeLevel = scopeLevel.parent; | |
| 5967 | 3631 | } | |
| 5968 | 25565 | scope = scope.parent.getNextOuterStmtLst(); | |
| 5969 | 25565 | } | |
| 5970 | 43048 | } | |
| 5971 | |||
| 5972 | 11816 | p IRGenerator.generateFctDecl(const model::Function* fct, const Vector<llvm::Value>& args) { | |
| 5973 | // Retrieve metadata for the function | ||
| 5974 | 11816 | const String mangledName = fct.getMangledName(); | |
| 5975 | |||
| 5976 | // Function is not defined in the current module -> declare it | ||
| 5977 | 11816 | const llvm::Function existingFct = this.module.getFunction(mangledName.getRaw()); | |
| 5978 | 11816 | if existingFct.isNull() { | |
| 5979 | 2429 | Vector<llvm::Type> paramTypes; | |
| 5980 | 5340 | foreach const llvm::Value& argValue : args { | |
| 5981 | 2911 | paramTypes.pushBack(argValue.getType()); | |
| 5982 | 2911 | } | |
| 5983 | 2429 | assert fct.isProcedure(); | |
| 5984 | 2429 | const llvm::Type fctType = llvm::getFunctionType(this.builder.getVoidTy(), paramTypes, false); | |
| 5985 | 2429 | this.module.getOrInsertFunction(mangledName.getRaw(), fctType); | |
| 5986 | |||
| 5987 | 2429 | if fct.isMethod() { | |
| 5988 | // Get callee function | ||
| 5989 | 2429 | llvm::Function callee = this.module.getFunction(mangledName.getRaw()); | |
| 5990 | 2429 | assert !callee.isNull(); | |
| 5991 | |||
| 5992 | // Set attributes to 'this' param | ||
| 5993 | 2429 | const llvm::Type structType = this.getThisStructType(fct); | |
| 5994 | 2429 | callee.addParamAttr(0, "noundef"); | |
| 5995 | 2429 | callee.addParamAttr(0, "nonnull"); | |
| 5996 | 2429 | callee.addDereferenceableParamAttr(0, this.module.getTypeStoreSize(structType)); | |
| 5997 | 2429 | callee.addParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(structType))); | |
| 5998 | 2429 | } | |
| 5999 | 2429 | } | |
| 6000 | 11816 | } | |
| 6001 | |||
| 6002 | 11816 | #[ignoreUnusedReturnValue] | |
| 6003 | 11816 | f<llvm::Value> IRGenerator.generateFctCall(const model::Function* fct, const Vector<llvm::Value>& args) { | |
| 6004 | // Retrieve metadata for the function | ||
| 6005 | 11816 | const String mangledName = fct.getMangledName(); | |
| 6006 | |||
| 6007 | // Get callee function | ||
| 6008 | 11816 | const llvm::Function callee = this.module.getFunction(mangledName.getRaw()); | |
| 6009 | 11816 | assert !callee.isNull(); | |
| 6010 | |||
| 6011 | // Generate function call | ||
| 6012 | 11816 | llvm::Value callInst = this.builder.createCall(callee, args); | |
| 6013 | |||
| 6014 | // Set attributes to 'this' param | ||
| 6015 | 11816 | if fct.isMethod() { | |
| 6016 | 11816 | const llvm::Type structType = this.getThisStructType(fct); | |
| 6017 | 11816 | callInst.addCallParamAttr(0, "noundef"); | |
| 6018 | 11816 | callInst.addCallParamAttr(0, "nonnull"); | |
| 6019 | 11816 | callInst.addCallDereferenceableParamAttr(0, this.module.getTypeStoreSize(structType)); | |
| 6020 | 11816 | callInst.addCallParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(structType))); | |
| 6021 | 11816 | } | |
| 6022 | |||
| 6023 | 23632 | return callInst; | |
| 6024 | 11816 | } | |
| 6025 | |||
| 6026 | /** | ||
| 6027 | * Get the LLVM type of the struct, that the 'this' param of the given method points to | ||
| 6028 | * | ||
| 6029 | * @param method Method | ||
| 6030 | * @return LLVM struct type | ||
| 6031 | */ | ||
| 6032 | 14245 | f<llvm::Type> IRGenerator.getThisStructType(const model::Function* method) { | |
| 6033 | 14245 | const SymbolTableEntry* thisEntry = method.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME)); | |
| 6034 | 14245 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 6035 | 14245 | const QualType& thisType = thisEntry.getQualType(); | |
| 6036 | 14245 | const QualType structSType = thisType.getContained(); | |
| 6037 | 14245 | const llvm::Type structType = structSType.toLLVMType(this.sourceFile); | |
| 6038 | 14245 | assert !structType.isNull(); | |
| 6039 | 28490 | return structType; | |
| 6040 | 14245 | } | |
| 6041 | |||
| 6042 | 11816 | p IRGenerator.generateProcDeclAndCall(const model::Function* proc, const Vector<llvm::Value>& args) { | |
| 6043 | 11816 | this.generateFctDecl(proc, args); | |
| 6044 | 11816 | this.generateFctCall(proc, args); | |
| 6045 | 11816 | } | |
| 6046 | |||
| 6047 | 9249 | p IRGenerator.generateCtorOrDtorCall(const SymbolTableEntry* entry, const model::Function* ctorOrDtor, const Vector<llvm::Value>& args) { | |
| 6048 | // Retrieve address of the struct variable. For fields this is the 'this' variable, otherwise use the normal address | ||
| 6049 | 9249 | llvm::Value structAddr; | |
| 6050 | 9249 | if entry.isField() { | |
| 6051 | // Take 'this' var as base pointer | ||
| 6052 | 1071 | const SymbolTableEntry* thisVar = this.currentScope.lookupStrict(String(THIS_VARIABLE_NAME)); | |
| 6053 | 1071 | assert thisVar != nil<const SymbolTableEntry*>; | |
| 6054 | 1071 | const QualType& thisVarType = thisVar.getQualType(); | |
| 6055 | 1071 | assert thisVarType.isPtr(); | |
| 6056 | 1071 | const QualType thisStructType = thisVarType.getContained(); | |
| 6057 | 1071 | assert thisStructType.is(SuperType::TY_STRUCT); | |
| 6058 | 1071 | const Struct* spiceStruct = thisStructType.getStruct(entry.declNode); | |
| 6059 | 1071 | assert spiceStruct != nil<const Struct*>; | |
| 6060 | 1071 | const llvm::Type thisType = thisStructType.toLLVMType(this.sourceFile); | |
| 6061 | 1071 | const llvm::Value thisPtr = this.insertLoad(this.builder.getPtrTy(), this.getAddress(thisVar)); | |
| 6062 | // Add field offset | ||
| 6063 | 1071 | structAddr = this.insertStructGEP(thisType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(entry.orderIndex))); | |
| 6064 | 1071 | } else { | |
| 6065 | 8178 | structAddr = this.getAddress(entry); | |
| 6066 | // For optional parameter initializers we need this exception | ||
| 6067 | 8178 | if structAddr.isNull() { | |
| 6068 | 624 | return; | |
| 6069 | } | ||
| 6070 | 7554 | } | |
| 6071 | 8625 | assert !structAddr.isNull(); | |
| 6072 | 8625 | this.generateCtorOrDtorCall(structAddr, ctorOrDtor, args); | |
| 6073 | 8625 | } | |
| 6074 | |||
| 6075 | 11816 | p IRGenerator.generateCtorOrDtorCall(llvm::Value structAddr, const model::Function* ctorOrDtor, const Vector<llvm::Value>& args) { | |
| 6076 | // Build parameter list | ||
| 6077 | 11816 | Vector<llvm::Value> argValues; | |
| 6078 | 11816 | argValues.pushBack(structAddr); | |
| 6079 | 13390 | foreach const llvm::Value& arg : args { | |
| 6080 | 1574 | argValues.pushBack(arg); | |
| 6081 | 1574 | } | |
| 6082 | |||
| 6083 | // Generate function call | ||
| 6084 | 11816 | this.generateProcDeclAndCall(ctorOrDtor, argValues); | |
| 6085 | 11816 | } | |
| 6086 | |||
| 6087 | /** | ||
| 6088 | * Copy an argument for a by-value param, that owns its argument (see QualType.isOwnedByValueParam). The called function | ||
| 6089 | * destructs the copy. | ||
| 6090 | * | ||
| 6091 | * @param originalPtr Address of the argument | ||
| 6092 | * @param valueType Type of the argument without reference | ||
| 6093 | * @param copyCtor Copy ctor of the argument type | ||
| 6094 | * @return Address of the copy | ||
| 6095 | */ | ||
| 6096 | 46 | public f<llvm::Value> IRGenerator.generateArgCopy(llvm::Value originalPtr, const QualType& valueType, const model::Function* copyCtor) { | |
| 6097 | 46 | const llvm::Value copyPtr = this.insertAlloca(valueType.toLLVMType(this.sourceFile), String("arg.copy")); | |
| 6098 | 46 | Vector<llvm::Value> ctorArgs; | |
| 6099 | 46 | ctorArgs.pushBack(originalPtr); | |
| 6100 | 46 | this.generateCtorOrDtorCall(copyPtr, copyCtor, ctorArgs); | |
| 6101 | 92 | return copyPtr; | |
| 6102 | 46 | } | |
| 6103 | |||
| 6104 | /** | ||
| 6105 | * Produce the argument value for a by-value param, that owns its argument (see QualType.isOwnedByValueParam), for calls, that | ||
| 6106 | * do not resolve the copy ctor in advance, like calls of operator functions or ctor calls via __new and __placement_new. | ||
| 6107 | * | ||
| 6108 | * @param originalPtr Address of the argument | ||
| 6109 | * @param paramType Type of the param | ||
| 6110 | * @return Value of the copy, which the called function destructs | ||
| 6111 | */ | ||
| 6112 | 3 | public f<llvm::Value> IRGenerator.generateOwnedArgValue(llvm::Value originalPtr, const QualType& paramType) { | |
| 6113 | 3 | const QualType valueType = paramType.toNonConst(); | |
| 6114 | 3 | ArgList args; | |
| 6115 | 3 | args.pushBack(Pair<QualType, bool>(valueType.toConstRef(nil<ASTNode*>), false)); | |
| 6116 | 3 | const model::Function* copyCtor = lookupFunction(valueType.getBodyScope(), String(CTOR_FUNCTION_NAME), valueType, args, true); | |
| 6117 | 3 | assert copyCtor != nil<const model::Function*>; // Resolved by the type checker | |
| 6118 | 3 | const llvm::Value copyPtr = this.generateArgCopy(originalPtr, valueType, copyCtor); | |
| 6119 | 6 | return this.insertLoad(valueType.toLLVMType(this.sourceFile), copyPtr); | |
| 6120 | 3 | } | |
| 6121 | |||
| 6122 | 691 | p IRGenerator.generateDeallocCall(llvm::Value variableAddress) { | |
| 6123 | // Abort if the address is not set. This can happen when leaving the scope of a dtor, which already freed the heap memory | ||
| 6124 | 691 | if variableAddress.isNull() { | |
| 6125 | 367 | return; | |
| 6126 | } | ||
| 6127 | |||
| 6128 | // In case of string runtime, call free manually. Otherwise, use the memory_rt implementation of sDealloc() | ||
| 6129 | 324 | Vector<llvm::Value> args; | |
| 6130 | 324 | args.pushBack(variableAddress); | |
| 6131 | 324 | if this.sourceFile.isStringRT() { | |
| 6132 | ✗ | const llvm::Function freeFct = this.stdFunctionManager.getFreeFct(); | |
| 6133 | ✗ | this.builder.createCall(freeFct, args); | |
| 6134 | ✗ | } else { | |
| 6135 | 324 | const llvm::Function deallocFct = this.stdFunctionManager.getDeallocBytePtrRefFct(); | |
| 6136 | 324 | this.builder.createCall(deallocFct, args); | |
| 6137 | 324 | } | |
| 6138 | 324 | } | |
| 6139 | |||
| 6140 | /** | ||
| 6141 | * Prologue of an implicit function or procedure. The body is generated between this prologue and endImplicitFunction() at the | ||
| 6142 | * call site. | ||
| 6143 | * | ||
| 6144 | * @param spiceFunc Spice function or procedure | ||
| 6145 | * @param isFunction Function or procedure | ||
| 6146 | * @return LLVM function or nil, if the function is unused and therefore not generated | ||
| 6147 | */ | ||
| 6148 | 1444 | f<llvm::Function> IRGenerator.beginImplicitFunction(const model::Function* spiceFunc, bool isFunction) { | |
| 6149 | // Only focus on method procedures | ||
| 6150 | 1444 | const ASTNode* node = spiceFunc.entry.declNode; | |
| 6151 | 1444 | if isFunction { | |
| 6152 | 9 | assert spiceFunc.isFunction(); | |
| 6153 | 9 | } else { | |
| 6154 | 1435 | assert node != nil<const ASTNode*>; | |
| 6155 | 1435 | assert spiceFunc.isProcedure(); | |
| 6156 | 1435 | } | |
| 6157 | |||
| 6158 | // Only generate if used | ||
| 6159 | 1444 | if !spiceFunc.used { | |
| 6160 | 714 | return llvm::Function(); | |
| 6161 | } | ||
| 6162 | |||
| 6163 | // Retrieve return type | ||
| 6164 | 730 | llvm::Type returnType = this.builder.getVoidTy(); | |
| 6165 | 730 | if isFunction { | |
| 6166 | 9 | returnType = spiceFunc.returnType.toLLVMType(this.sourceFile); | |
| 6167 | 9 | } | |
| 6168 | |||
| 6169 | // Get 'this' entry | ||
| 6170 | 730 | Vector<llvm::Type> paramTypes; | |
| 6171 | 730 | const SymbolTableEntry* thisEntry = nil<const SymbolTableEntry*>; | |
| 6172 | 730 | if spiceFunc.isMethod() { | |
| 6173 | 721 | thisEntry = spiceFunc.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME)); | |
| 6174 | 721 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 6175 | 721 | paramTypes.pushBack(this.builder.getPtrTy()); | |
| 6176 | 721 | } | |
| 6177 | |||
| 6178 | // Get parameter types | ||
| 6179 | 872 | foreach const model::Param& param : spiceFunc.paramList { | |
| 6180 | 142 | assert !param.isOptional; | |
| 6181 | 142 | paramTypes.pushBack(param.qualType.toLLVMType(this.sourceFile)); | |
| 6182 | 142 | } | |
| 6183 | |||
| 6184 | // Get function linkage | ||
| 6185 | 730 | const QualType& entryType = spiceFunc.entry.getQualType(); | |
| 6186 | 730 | const bool isPublic = entryType.isPublic(); | |
| 6187 | |||
| 6188 | // Create function or implement a forward declaration that an earlier call site already inserted for this mangled | ||
| 6189 | // name (e.g. another struct's ctor preamble calling this default member before it got its own definition here). | ||
| 6190 | // Unconditionally calling llvm::Function::Create would not find that declaration - LLVM auto-renames the new, | ||
| 6191 | // colliding GlobalValue instead of merging with it, leaving the original declaration (and every call to it) | ||
| 6192 | // permanently undefined. | ||
| 6193 | 730 | const String mangledName = spiceFunc.getMangledName(); | |
| 6194 | 730 | const llvm::Type fctType = llvm::getFunctionType(returnType, paramTypes, false); | |
| 6195 | 730 | this.module.getOrInsertFunction(mangledName.getRaw(), fctType); | |
| 6196 | 730 | llvm::Function fct = this.module.getFunction(mangledName.getRaw()); | |
| 6197 | 730 | assert fct.isEmpty(); | |
| 6198 | 730 | fct.setLinkage(this.getSymbolLinkageType(isPublic)); | |
| 6199 | 730 | fct.addFnAttr("mustprogress"); | |
| 6200 | 730 | this.addCommonFctAttrs(fct); | |
| 6201 | |||
| 6202 | // Set attributes to 'this' param | ||
| 6203 | 730 | if spiceFunc.isMethod() { | |
| 6204 | 721 | fct.addParamAttr(0, "noundef"); | |
| 6205 | 721 | fct.addParamAttr(0, "nonnull"); | |
| 6206 | 721 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 6207 | 721 | const QualType& thisType = thisEntry.getQualType(); | |
| 6208 | 721 | const QualType structSType = thisType.getContained(); | |
| 6209 | 721 | const llvm::Type structType = structSType.toLLVMType(this.sourceFile); | |
| 6210 | 721 | assert !structType.isNull(); | |
| 6211 | 721 | fct.addDereferenceableParamAttr(0, this.module.getTypeStoreSize(structType)); | |
| 6212 | 721 | fct.addParamAttr(0, "align", cast<unsigned long>(this.module.getABITypeAlign(structType))); | |
| 6213 | 721 | } | |
| 6214 | |||
| 6215 | // Add debug info | ||
| 6216 | 730 | this.diGenerator.generateFunctionDebugInfo(fct, spiceFunc); | |
| 6217 | 730 | if node != nil<const ASTNode*> { | |
| 6218 | 721 | this.diGenerator.setSourceLocation(node); | |
| 6219 | 721 | } | |
| 6220 | |||
| 6221 | // Change to body scope | ||
| 6222 | 730 | this.compilerPass.changeToScope(spiceFunc.getScopeName(), ScopeType::FUNC_PROC_BODY); | |
| 6223 | |||
| 6224 | // Create entry block | ||
| 6225 | 730 | const llvm::BasicBlock bEntry = this.createBlock(); | |
| 6226 | 730 | this.switchToBlock(bEntry, fct); | |
| 6227 | |||
| 6228 | // Reset alloca insert markers to this block | ||
| 6229 | 730 | this.allocaInsertBlock = bEntry; | |
| 6230 | 730 | this.allocaInsertInst = llvm::Value(); | |
| 6231 | |||
| 6232 | // Store first argument to 'this' symbol | ||
| 6233 | 730 | if spiceFunc.isMethod() { | |
| 6234 | 721 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 6235 | // Allocate space for the parameter | ||
| 6236 | 721 | const llvm::Value thisAddress = this.insertAlloca(paramTypes.front(), String(THIS_VARIABLE_NAME)); | |
| 6237 | // Update the symbol table entry | ||
| 6238 | 721 | this.updateAddress(thisEntry, thisAddress); | |
| 6239 | // Generate debug info | ||
| 6240 | 721 | this.diGenerator.generateLocalVarDebugInfo(String(THIS_VARIABLE_NAME), thisAddress, 1l); | |
| 6241 | // Store the value at the new address | ||
| 6242 | 721 | this.insertStore(fct.getArg(0), thisAddress); | |
| 6243 | 721 | } | |
| 6244 | |||
| 6245 | 1460 | return fct; | |
| 6246 | 730 | } | |
| 6247 | |||
| 6248 | /** | ||
| 6249 | * Epilogue of an implicit function or procedure, see beginImplicitFunction() | ||
| 6250 | * | ||
| 6251 | * @param spiceFunc Spice function or procedure | ||
| 6252 | * @param fct LLVM function | ||
| 6253 | * @param isFunction Function or procedure | ||
| 6254 | */ | ||
| 6255 | 730 | p IRGenerator.endImplicitFunction(const model::Function* spiceFunc, llvm::Function fct, bool isFunction) { | |
| 6256 | // Create return instruction | ||
| 6257 | 730 | if !isFunction { | |
| 6258 | 721 | this.builder.createRetVoid(); | |
| 6259 | 721 | } | |
| 6260 | |||
| 6261 | // Conclude debug info for function | ||
| 6262 | 730 | this.diGenerator.concludeFunctionDebugInfo(); | |
| 6263 | |||
| 6264 | // Verify function | ||
| 6265 | // Use the code location of the declaration node if available. Otherwise, (e.g. in case of test main) use an artificial code loc | ||
| 6266 | 730 | const ASTNode* node = spiceFunc.entry.declNode; | |
| 6267 | 730 | const CodeLoc codeLoc = node != nil<const ASTNode*> ? node.codeLoc : CodeLoc(1l, 1l, this.sourceFile.filePath.toString()); | |
| 6268 | 730 | this.verifyFunction(fct, codeLoc); | |
| 6269 | |||
| 6270 | // Change to parent scope | ||
| 6271 | 730 | this.compilerPass.changeToParentScope(ScopeType::FUNC_PROC_BODY); | |
| 6272 | 730 | } | |
| 6273 | |||
| 6274 | 5495 | p IRGenerator.generateCtorBodyPreamble(Scope* bodyScope) { | |
| 6275 | // Retrieve struct scope | ||
| 6276 | 5495 | Scope* structScope = bodyScope.parent; | |
| 6277 | 5495 | assert structScope != nil<Scope*>; | |
| 6278 | |||
| 6279 | // Get struct address | ||
| 6280 | 5495 | const SymbolTableEntry* thisEntry = bodyScope.lookupStrict(String(THIS_VARIABLE_NAME)); | |
| 6281 | 5495 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 6282 | 5495 | const llvm::Value thisPtrPtr = this.getAddress(thisEntry); | |
| 6283 | 5495 | assert !thisPtrPtr.isNull(); | |
| 6284 | 5495 | llvm::Value thisPtr; | |
| 6285 | 5495 | const QualType& thisType = thisEntry.getQualType(); | |
| 6286 | 5495 | const QualType structSymbolType = thisType.getBase(); | |
| 6287 | 5495 | const llvm::Type structType = structSymbolType.toLLVMType(this.sourceFile); | |
| 6288 | |||
| 6289 | // Store VTable to first struct field if required | ||
| 6290 | 5495 | const Struct* spiceStruct = structSymbolType.getStruct(nil<ASTNode*>); | |
| 6291 | 5495 | assert spiceStruct != nil<const Struct*>; | |
| 6292 | 5495 | if !spiceStruct.vTableData.vtable.isNull() { | |
| 6293 | 1170 | assert !spiceStruct.vTableData.vtableType.isNull(); | |
| 6294 | // Store VTable to field address at index 0 | ||
| 6295 | 1170 | thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr); | |
| 6296 | 1170 | Vector<llvm::Value> indices; | |
| 6297 | 1170 | indices.pushBack(this.getInt64(0l)); | |
| 6298 | 1170 | indices.pushBack(this.getInt32(0l)); | |
| 6299 | 1170 | indices.pushBack(this.getInt32(2l)); | |
| 6300 | 1170 | const llvm::Value gepResult = this.insertInBoundsGEP(spiceStruct.vTableData.vtableType, spiceStruct.vTableData.vtable, indices); | |
| 6301 | 1170 | this.insertStore(gepResult, thisPtr); | |
| 6302 | 1170 | } | |
| 6303 | |||
| 6304 | 5495 | const unsigned long fieldCount = structScope.getFieldCount(); | |
| 6305 | 5495 | const Vector<llvm::Value> noArgs; | |
| 6306 | 20094 | for unsigned long fieldIdx = 0l; fieldIdx < fieldCount; fieldIdx++ { | |
| 6307 | 14599 | const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx)); | |
| 6308 | 14599 | assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField(); | |
| 6309 | 14599 | if fieldSymbol.isImplicitField { | |
| 6310 | 1004 | continue; | |
| 6311 | } | ||
| 6312 | |||
| 6313 | // Call ctor for struct fields | ||
| 6314 | 13595 | const QualType& fieldType = fieldSymbol.getQualType(); | |
| 6315 | 13595 | const ASTFieldNode* fieldNode = downcast<ASTFieldNode>(fieldSymbol.declNode); | |
| 6316 | 13595 | if fieldType.is(SuperType::TY_STRUCT) { | |
| 6317 | // Lookup ctor function and call if available | ||
| 6318 | 2278 | Scope* matchScope = fieldType.getBodyScope(); | |
| 6319 | 2278 | const ArgList noCtorArgs; | |
| 6320 | 2278 | const model::Function* ctorFunction = lookupFunction(matchScope, String(CTOR_FUNCTION_NAME), fieldType, noCtorArgs, false); | |
| 6321 | 2278 | if ctorFunction != nil<const model::Function*> { | |
| 6322 | 2100 | if thisPtr.isNull() { | |
| 6323 | 423 | thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr); | |
| 6324 | 423 | } | |
| 6325 | 2100 | const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx))); | |
| 6326 | 2100 | this.generateCtorOrDtorCall(fieldAddress, ctorFunction, noArgs); | |
| 6327 | 2100 | } | |
| 6328 | 2278 | continue; | |
| 6329 | 2278 | } | |
| 6330 | |||
| 6331 | // Store default field values | ||
| 6332 | 11317 | if fieldNode.defaultValue != nil<IVisitable*> || this.cliOptions.buildMode != BuildMode::RELEASE { | |
| 6333 | // Retrieve field address | ||
| 6334 | 11317 | if thisPtr.isNull() { | |
| 6335 | 3849 | thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr); | |
| 6336 | 3849 | } | |
| 6337 | 11317 | const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx))); | |
| 6338 | // Retrieve default value | ||
| 6339 | 11317 | llvm::Value value; | |
| 6340 | 11317 | if fieldNode.defaultValue != nil<IVisitable*> { | |
| 6341 | // To resolve the default value, we need to temporarily change to the manifestation of the current struct instantiation | ||
| 6342 | 6057 | const unsigned long oldManIdx = this.manIdx; // Save manifestation index | |
| 6343 | 6057 | this.manIdx = spiceStruct.manifestationIndex; | |
| 6344 | 6057 | value = this.resolveValue(fieldNode.defaultValue); | |
| 6345 | 6057 | this.manIdx = oldManIdx; // Restore manifestation index | |
| 6346 | 6057 | } else { | |
| 6347 | 5260 | assert this.cliOptions.buildMode == BuildMode::DEBUG || this.cliOptions.buildMode == BuildMode::TEST; | |
| 6348 | 5260 | value = this.getDefaultValueForSymbolType(fieldType); | |
| 6349 | 5260 | } | |
| 6350 | // Store default value | ||
| 6351 | 11317 | this.insertStore(value, fieldAddress); | |
| 6352 | 11317 | } | |
| 6353 | 11317 | } | |
| 6354 | 5495 | } | |
| 6355 | |||
| 6356 | 161 | p IRGenerator.generateDefaultCtor(const model::Function* ctorFunction) { | |
| 6357 | 161 | assert ctorFunction.implicitDefault && ctorFunction.name == CTOR_FUNCTION_NAME; | |
| 6358 | 161 | const llvm::Function fct = this.beginImplicitFunction(ctorFunction, false); | |
| 6359 | 161 | if fct.isNull() { | |
| 6360 | 38 | return; | |
| 6361 | } | ||
| 6362 | 123 | this.generateCtorBodyPreamble(ctorFunction.bodyScope); | |
| 6363 | 123 | this.endImplicitFunction(ctorFunction, fct, false); | |
| 6364 | 123 | } | |
| 6365 | |||
| 6366 | 133 | p IRGenerator.generateCopyCtorBodyPreamble(const model::Function* copyCtorFunction) { | |
| 6367 | // Retrieve struct scope | ||
| 6368 | 133 | Scope* structScope = copyCtorFunction.bodyScope.parent; | |
| 6369 | 133 | assert structScope != nil<Scope*>; | |
| 6370 | |||
| 6371 | // Get struct address | ||
| 6372 | 133 | const SymbolTableEntry* thisEntry = copyCtorFunction.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME)); | |
| 6373 | 133 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 6374 | 133 | const llvm::Value thisPtrPtr = this.getAddress(thisEntry); | |
| 6375 | 133 | assert !thisPtrPtr.isNull(); | |
| 6376 | 133 | llvm::Value thisPtr; | |
| 6377 | 133 | const QualType& thisType = thisEntry.getQualType(); | |
| 6378 | 133 | const QualType structSType = thisType.getBase(); | |
| 6379 | 133 | const llvm::Type structType = structSType.toLLVMType(this.sourceFile); | |
| 6380 | 133 | const Struct* spiceStruct = structSType.getStruct(nil<ASTNode*>); | |
| 6381 | 133 | assert spiceStruct != nil<const Struct*>; | |
| 6382 | |||
| 6383 | // Retrieve the value of the original struct, which is the only function parameter | ||
| 6384 | 133 | const llvm::BasicBlock insertBlock = this.builder.getInsertBlock(); | |
| 6385 | 133 | const llvm::Function parentFct = insertBlock.getParent(); | |
| 6386 | 133 | const llvm::Value originalThisPtr = parentFct.getArg(1); | |
| 6387 | |||
| 6388 | // The synthesized vtable pointer is no field, so take it over separately | ||
| 6389 | 133 | if spiceStruct.hasSynthesizedVTablePtr() { | |
| 6390 | 2 | thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr); | |
| 6391 | 2 | const llvm::Value vtablePtr = this.insertLoad(this.builder.getPtrTy(), originalThisPtr); | |
| 6392 | 2 | this.insertStore(vtablePtr, thisPtr); | |
| 6393 | 2 | } | |
| 6394 | |||
| 6395 | 133 | const unsigned long fieldCount = structScope.getFieldCount(); | |
| 6396 | 749 | for unsigned long fieldIdx = 0l; fieldIdx < fieldCount; fieldIdx++ { | |
| 6397 | 616 | const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx)); | |
| 6398 | 616 | assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField(); | |
| 6399 | |||
| 6400 | // Retrieve the address of the original field (copy source) | ||
| 6401 | 616 | const llvm::Value originalFieldAddress = this.insertStructGEP(structType, originalThisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx))); | |
| 6402 | |||
| 6403 | 616 | const QualType& fieldType = fieldSymbol.getQualType(); | |
| 6404 | |||
| 6405 | // Call copy ctor for struct fields | ||
| 6406 | 616 | if fieldType.is(SuperType::TY_STRUCT) && !fieldType.isTriviallyCopyable(nil<ASTNode*>) { | |
| 6407 | // Lookup copy ctor function and call if available | ||
| 6408 | 393 | Scope* matchScope = fieldType.getBodyScope(); | |
| 6409 | 393 | ArgList args; | |
| 6410 | 393 | args.pushBack(Pair<QualType, bool>(fieldType.toConstRef(nil<ASTNode*>), false /* we have the field as storage */)); | |
| 6411 | 393 | const model::Function* copyCtor = lookupFunction(matchScope, String(CTOR_FUNCTION_NAME), fieldType, args, false); | |
| 6412 | 393 | assert copyCtor != nil<const model::Function*>; | |
| 6413 | 393 | Vector<llvm::Value> ctorArgs; | |
| 6414 | 393 | ctorArgs.pushBack(originalFieldAddress); | |
| 6415 | 393 | this.generateCtorOrDtorCall(fieldSymbol, copyCtor, ctorArgs); | |
| 6416 | 393 | continue; | |
| 6417 | 393 | } | |
| 6418 | |||
| 6419 | // Retrieve the address of the new field (copy dest) | ||
| 6420 | 223 | if thisPtr.isNull() { | |
| 6421 | 98 | thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr); | |
| 6422 | 98 | } | |
| 6423 | 223 | const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx))); | |
| 6424 | |||
| 6425 | // For owning heap fields, copy the underlying heap storage | ||
| 6426 | 223 | if fieldType.isHeap() { | |
| 6427 | 6 | assert fieldType.isPtr(); | |
| 6428 | 6 | const QualType pointeeSType = fieldType.getContained(); | |
| 6429 | 6 | const llvm::Type pointeeType = pointeeSType.toLLVMType(this.sourceFile); | |
| 6430 | |||
| 6431 | // Retrieve original heap address | ||
| 6432 | 6 | const llvm::Value originalHeapAddress = this.insertLoad(this.builder.getPtrTy(), originalFieldAddress); | |
| 6433 | |||
| 6434 | // Initialize the new field with nullptr, so that it stays valid if the original heap address is nullptr | ||
| 6435 | 6 | this.insertStore(llvm::getNullValue(this.builder.getPtrTy()), fieldAddress); | |
| 6436 | |||
| 6437 | // Insert check for nullptr | ||
| 6438 | 6 | const llvm::BasicBlock bThen = this.createBlock(String("nullptrcheck.then")); | |
| 6439 | 6 | const llvm::BasicBlock bExit = this.createBlock(String("nullptrcheck.exit")); | |
| 6440 | 6 | const llvm::Value condValue = this.builder.createICmp(llvm::LLVMIntPredicate::NE, originalHeapAddress, llvm::getNullValue(this.builder.getPtrTy())); | |
| 6441 | 6 | this.insertCondJump(condValue, bThen, bExit); | |
| 6442 | |||
| 6443 | // Fill then block | ||
| 6444 | 6 | this.switchToBlock(bThen); | |
| 6445 | |||
| 6446 | // Allocate new space on the heap | ||
| 6447 | 6 | const llvm::Function unsafeAllocFct = this.stdFunctionManager.getAllocUnsafeLongFct(); | |
| 6448 | 6 | const unsigned long typeSizeInBytes = this.module.getTypeSizeInBits(pointeeType) / 8l; | |
| 6449 | 6 | Vector<llvm::Value> allocArgs; | |
| 6450 | 6 | allocArgs.pushBack(this.builder.getInt64(typeSizeInBytes)); | |
| 6451 | 6 | const llvm::Value newHeapAddress = this.builder.createCall(unsafeAllocFct, allocArgs); | |
| 6452 | 6 | this.insertStore(newHeapAddress, fieldAddress); | |
| 6453 | |||
| 6454 | // Copy data from the old heap storage to the new one | ||
| 6455 | 6 | this.generateShallowCopy(originalHeapAddress, pointeeType, newHeapAddress, false); | |
| 6456 | 6 | this.insertJump(bExit); | |
| 6457 | |||
| 6458 | // Switch to exit block | ||
| 6459 | 6 | this.switchToBlock(bExit); | |
| 6460 | |||
| 6461 | 6 | continue; | |
| 6462 | 6 | } | |
| 6463 | |||
| 6464 | // Shallow copy | ||
| 6465 | 217 | const llvm::Type fieldLLVMType = fieldType.toLLVMType(this.sourceFile); | |
| 6466 | 217 | this.generateShallowCopy(originalFieldAddress, fieldLLVMType, fieldAddress, false); | |
| 6467 | 217 | } | |
| 6468 | 133 | } | |
| 6469 | |||
| 6470 | 700 | p IRGenerator.generateDefaultCopyCtor(const model::Function* copyCtorFunction) { | |
| 6471 | 700 | assert copyCtorFunction.implicitDefault && copyCtorFunction.name == CTOR_FUNCTION_NAME; | |
| 6472 | 700 | const llvm::Function fct = this.beginImplicitFunction(copyCtorFunction, false); | |
| 6473 | 700 | if fct.isNull() { | |
| 6474 | 567 | return; | |
| 6475 | } | ||
| 6476 | 133 | this.generateCopyCtorBodyPreamble(copyCtorFunction); | |
| 6477 | 133 | this.endImplicitFunction(copyCtorFunction, fct, false); | |
| 6478 | 133 | } | |
| 6479 | |||
| 6480 | 9 | p IRGenerator.generateMoveCtorBodyPreamble(const model::Function* moveCtorFunction) { | |
| 6481 | // Retrieve struct scope | ||
| 6482 | 9 | Scope* structScope = moveCtorFunction.bodyScope.parent; | |
| 6483 | 9 | assert structScope != nil<Scope*>; | |
| 6484 | |||
| 6485 | // Get struct address | ||
| 6486 | 9 | const SymbolTableEntry* thisEntry = moveCtorFunction.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME)); | |
| 6487 | 9 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 6488 | 9 | const llvm::Value thisPtrPtr = this.getAddress(thisEntry); | |
| 6489 | 9 | assert !thisPtrPtr.isNull(); | |
| 6490 | 9 | llvm::Value thisPtr; | |
| 6491 | 9 | const QualType& thisType = thisEntry.getQualType(); | |
| 6492 | 9 | const QualType structSType = thisType.getBase(); | |
| 6493 | 9 | const llvm::Type structType = structSType.toLLVMType(this.sourceFile); | |
| 6494 | 9 | const Struct* spiceStruct = structSType.getStruct(nil<ASTNode*>); | |
| 6495 | 9 | assert spiceStruct != nil<const Struct*>; | |
| 6496 | |||
| 6497 | // Retrieve the value of the original (source) struct, which is the only function parameter | ||
| 6498 | 9 | const llvm::BasicBlock insertBlock = this.builder.getInsertBlock(); | |
| 6499 | 9 | const llvm::Function parentFct = insertBlock.getParent(); | |
| 6500 | 9 | const llvm::Value originalThisPtr = parentFct.getArg(1); | |
| 6501 | |||
| 6502 | // The synthesized vtable pointer is no field, so take it over separately | ||
| 6503 | 9 | if spiceStruct.hasSynthesizedVTablePtr() { | |
| 6504 | 2 | thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr); | |
| 6505 | 2 | const llvm::Value vtablePtr = this.insertLoad(this.builder.getPtrTy(), originalThisPtr); | |
| 6506 | 2 | this.insertStore(vtablePtr, thisPtr); | |
| 6507 | 2 | } | |
| 6508 | |||
| 6509 | 9 | const unsigned long fieldCount = structScope.getFieldCount(); | |
| 6510 | 25 | for unsigned long fieldIdx = 0l; fieldIdx < fieldCount; fieldIdx++ { | |
| 6511 | 16 | const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx)); | |
| 6512 | 16 | assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField(); | |
| 6513 | |||
| 6514 | // Retrieve the address of the original field (move source) | ||
| 6515 | 16 | const llvm::Value originalFieldAddress = this.insertStructGEP(structType, originalThisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx))); | |
| 6516 | |||
| 6517 | 16 | const QualType& fieldType = fieldSymbol.getQualType(); | |
| 6518 | |||
| 6519 | // Call move ctor for struct fields if available, otherwise fall back to copy or shallow copy | ||
| 6520 | 16 | if fieldType.is(SuperType::TY_STRUCT) { | |
| 6521 | 3 | Scope* matchScope = fieldType.getBodyScope(); | |
| 6522 | // First try to find a move ctor (non-const ref param). We scan the manifestations directly via | ||
| 6523 | // findMoveCtor rather than FunctionManager::lookup with a non-const ref arg, because lookup permits | ||
| 6524 | // const-param-to-non-const-arg "constify" matching and may return the copy ctor as a false positive. | ||
| 6525 | 3 | const model::Function* moveCtor = findMoveCtor(matchScope); | |
| 6526 | 3 | if moveCtor != nil<const model::Function*> { | |
| 6527 | 3 | Vector<llvm::Value> ctorArgs; | |
| 6528 | 3 | ctorArgs.pushBack(originalFieldAddress); | |
| 6529 | 3 | this.generateCtorOrDtorCall(fieldSymbol, moveCtor, ctorArgs); | |
| 6530 | 3 | continue; | |
| 6531 | 3 | } | |
| 6532 | // No move ctor: fall back to copy ctor for non-trivially copyable types | ||
| 6533 | ✗ | if !fieldType.isTriviallyCopyable(nil<ASTNode*>) { | |
| 6534 | ✗ | ArgList copyArgs; | |
| 6535 | ✗ | copyArgs.pushBack(Pair<QualType, bool>(fieldType.toConstRef(nil<ASTNode*>), false)); | |
| 6536 | ✗ | const model::Function* copyCtor = lookupFunction(matchScope, String(CTOR_FUNCTION_NAME), fieldType, copyArgs, false); | |
| 6537 | ✗ | assert copyCtor != nil<const model::Function*>; | |
| 6538 | ✗ | Vector<llvm::Value> ctorArgs; | |
| 6539 | ✗ | ctorArgs.pushBack(originalFieldAddress); | |
| 6540 | ✗ | this.generateCtorOrDtorCall(fieldSymbol, copyCtor, ctorArgs); | |
| 6541 | ✗ | continue; | |
| 6542 | ✗ | } | |
| 6543 | ✗ | } | |
| 6544 | |||
| 6545 | // Retrieve the address of the new field (move dest) | ||
| 6546 | 13 | if thisPtr.isNull() { | |
| 6547 | 5 | thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr); | |
| 6548 | 5 | } | |
| 6549 | 13 | const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx))); | |
| 6550 | |||
| 6551 | // For owning heap fields, transfer ownership: copy the pointer to the destination, and null out the source | ||
| 6552 | 13 | if fieldType.isHeap() { | |
| 6553 | 6 | assert fieldType.isPtr(); | |
| 6554 | |||
| 6555 | // Load original heap address | ||
| 6556 | 6 | const llvm::Value originalHeapAddress = this.insertLoad(this.builder.getPtrTy(), originalFieldAddress); | |
| 6557 | // Store it in the destination field | ||
| 6558 | 6 | this.insertStore(originalHeapAddress, fieldAddress); | |
| 6559 | // Null out the source field so its dtor does not free the storage | ||
| 6560 | 6 | this.insertStore(llvm::getNullValue(this.builder.getPtrTy()), originalFieldAddress); | |
| 6561 | |||
| 6562 | 6 | continue; | |
| 6563 | } | ||
| 6564 | |||
| 6565 | // Shallow copy non-heap, non-struct (or trivially copyable struct) fields | ||
| 6566 | 7 | const llvm::Type fieldLLVMType = fieldType.toLLVMType(this.sourceFile); | |
| 6567 | 7 | this.generateShallowCopy(originalFieldAddress, fieldLLVMType, fieldAddress, false); | |
| 6568 | 7 | } | |
| 6569 | 9 | } | |
| 6570 | |||
| 6571 | 21 | p IRGenerator.generateDefaultMoveCtor(const model::Function* moveCtorFunction) { | |
| 6572 | 21 | assert moveCtorFunction.implicitDefault && moveCtorFunction.name == CTOR_FUNCTION_NAME; | |
| 6573 | 21 | const llvm::Function fct = this.beginImplicitFunction(moveCtorFunction, false); | |
| 6574 | 21 | if fct.isNull() { | |
| 6575 | 12 | return; | |
| 6576 | } | ||
| 6577 | 9 | this.generateMoveCtorBodyPreamble(moveCtorFunction); | |
| 6578 | 9 | this.endImplicitFunction(moveCtorFunction, fct, false); | |
| 6579 | 9 | } | |
| 6580 | |||
| 6581 | 456 | p IRGenerator.generateDtorBodyPreamble(const model::Function* dtorFunction) { | |
| 6582 | // Retrieve struct scope | ||
| 6583 | 456 | Scope* structScope = dtorFunction.bodyScope.parent; | |
| 6584 | 456 | assert structScope != nil<Scope*>; | |
| 6585 | |||
| 6586 | // Get struct address | ||
| 6587 | 456 | const SymbolTableEntry* thisEntry = dtorFunction.bodyScope.lookupStrict(String(THIS_VARIABLE_NAME)); | |
| 6588 | 456 | assert thisEntry != nil<const SymbolTableEntry*>; | |
| 6589 | 456 | const llvm::Value thisPtrPtr = this.getAddress(thisEntry); | |
| 6590 | 456 | assert !thisPtrPtr.isNull(); | |
| 6591 | 456 | llvm::Value thisPtr; | |
| 6592 | 456 | const QualType& thisType = thisEntry.getQualType(); | |
| 6593 | 456 | const QualType structSType = thisType.getBase(); | |
| 6594 | 456 | const llvm::Type structType = structSType.toLLVMType(this.sourceFile); | |
| 6595 | 456 | const Struct* spiceStruct = structSType.getStruct(nil<ASTNode*>); | |
| 6596 | 456 | assert spiceStruct != nil<const Struct*>; | |
| 6597 | |||
| 6598 | 456 | const unsigned long fieldCount = structScope.getFieldCount(); | |
| 6599 | 456 | const Vector<llvm::Value> noArgs; | |
| 6600 | 1866 | for unsigned long i = 0l; i < fieldCount; i++ { | |
| 6601 | 1410 | const unsigned long fieldIdx = fieldCount - 1l - i; // Destruct fields in reverse order | |
| 6602 | 1410 | const SymbolTableEntry* fieldSymbol = structScope.lookupField(cast<unsigned int>(fieldIdx)); | |
| 6603 | 1410 | assert fieldSymbol != nil<const SymbolTableEntry*> && fieldSymbol.isField(); | |
| 6604 | |||
| 6605 | // Call dtor for struct fields | ||
| 6606 | 1410 | const QualType& fieldType = fieldSymbol.getQualType(); | |
| 6607 | 1410 | if fieldType.is(SuperType::TY_STRUCT) { | |
| 6608 | // Lookup dtor function and generate call if found | ||
| 6609 | 756 | const ArgList noDtorArgs; | |
| 6610 | 756 | const model::Function* dtorFct = lookupFunction(fieldType.getBodyScope(), String(DTOR_FUNCTION_NAME), fieldType, noDtorArgs, false); | |
| 6611 | 756 | if dtorFct != nil<const model::Function*> { | |
| 6612 | 609 | this.generateCtorOrDtorCall(fieldSymbol, dtorFct, noArgs); | |
| 6613 | 609 | } | |
| 6614 | 756 | continue; | |
| 6615 | 756 | } | |
| 6616 | |||
| 6617 | // Deallocate fields, that are stored on the heap | ||
| 6618 | 654 | if fieldType.isHeap() { | |
| 6619 | // Retrieve field address | ||
| 6620 | 201 | if thisPtr.isNull() { | |
| 6621 | 178 | thisPtr = this.insertLoad(this.builder.getPtrTy(), thisPtrPtr); | |
| 6622 | 178 | } | |
| 6623 | 201 | const llvm::Value fieldAddress = this.insertStructGEP(structType, thisPtr, cast<unsigned int>(spiceStruct.getFieldElementIndex(fieldIdx))); | |
| 6624 | // Call dealloc function | ||
| 6625 | 201 | this.generateDeallocCall(fieldAddress); | |
| 6626 | 201 | } | |
| 6627 | 654 | } | |
| 6628 | 456 | } | |
| 6629 | |||
| 6630 | 553 | p IRGenerator.generateDefaultDtor(const model::Function* dtorFunction) { | |
| 6631 | 553 | assert dtorFunction.implicitDefault && dtorFunction.name == DTOR_FUNCTION_NAME; | |
| 6632 | 553 | const llvm::Function fct = this.beginImplicitFunction(dtorFunction, false); | |
| 6633 | 553 | if fct.isNull() { | |
| 6634 | 97 | return; | |
| 6635 | } | ||
| 6636 | 456 | this.generateDtorBodyPreamble(dtorFunction); | |
| 6637 | 456 | this.endImplicitFunction(dtorFunction, fct, false); | |
| 6638 | 456 | } | |
| 6639 | |||
| 6640 | 9 | p IRGenerator.generateTestMain() { | |
| 6641 | // Collect all test functions. Iterate the source files in creation order, so that the tests of the main source file run | ||
| 6642 | // first, followed by the ones of its dependencies in import order. The order of the source file map depends on the hash | ||
| 6643 | // function | ||
| 6644 | 9 | Vector<const Vector<const model::Function*>*> tests; | |
| 6645 | 20 | foreach const SourceFile* testSourceFile : this.resourceManager.sourceFilesInCreationOrder { | |
| 6646 | 11 | if !testSourceFile.testFunctions.isEmpty() { | |
| 6647 | 11 | tests.pushBack(&testSourceFile.testFunctions); | |
| 6648 | 11 | } | |
| 6649 | 11 | } | |
| 6650 | |||
| 6651 | // Prepare printf function | ||
| 6652 | 9 | const llvm::Function printfFct = this.stdFunctionManager.getPrintfFct(); | |
| 6653 | |||
| 6654 | // Prepare success and error messages | ||
| 6655 | 9 | const CodeLoc& rootCodeLoc = *this.rootScope.codeLoc; | |
| 6656 | 9 | const llvm::Value allStartMsg = this.createGlobalStringConst(String("allStartMsg"), String(TEST_ALL_START_MSG), rootCodeLoc); | |
| 6657 | 9 | const llvm::Value allEndMsg = this.createGlobalStringConst(String("allEndMsg"), String(TEST_ALL_END_MSG), rootCodeLoc); | |
| 6658 | 9 | const llvm::Value fileStartMsg = this.createGlobalStringConst(String("fileStartMsg"), String(TEST_FILE_START_MSG), rootCodeLoc); | |
| 6659 | 9 | const llvm::Value fileEndMsg = this.createGlobalStringConst(String("fileEndMsg"), String(TEST_FILE_END_MSG), rootCodeLoc); | |
| 6660 | 9 | const llvm::Value runMsg = this.createGlobalStringConst(String("runMsg"), String(TEST_CASE_RUN_MSG), rootCodeLoc); | |
| 6661 | 9 | const llvm::Value successMsg = this.createGlobalStringConst(String("successMsg"), String(TEST_CASE_SUCCESS_MSG), rootCodeLoc); | |
| 6662 | 9 | const llvm::Value errorMsg = this.createGlobalStringConst(String("errorMsg"), String(TEST_CASE_FAILED_MSG), rootCodeLoc); | |
| 6663 | 9 | const llvm::Value skippedMsg = this.createGlobalStringConst(String("skippedMsg"), String(TEST_CASE_SKIPPED_MSG), rootCodeLoc); | |
| 6664 | |||
| 6665 | // Prepare entry for test main | ||
| 6666 | 9 | QualType functionType = QualType(SuperType::TY_FUNCTION); | |
| 6667 | 9 | functionType.setQualifiers(getTypeQualifiersOf(SuperType::TY_FUNCTION)); | |
| 6668 | 9 | functionType.makePublic(); | |
| 6669 | 9 | SymbolTableEntry entry = SymbolTableEntry(String(MAIN_FUNCTION_NAME), functionType, this.rootScope, nil<ASTNode*>, 0l, false); | |
| 6670 | |||
| 6671 | // Prepare test main function | ||
| 6672 | 9 | const model::ParamList noParams; | |
| 6673 | 9 | const Vector<GenericType> noTemplateTypes; | |
| 6674 | 9 | model::Function testMain = model::Function(String(MAIN_FUNCTION_NAME), &entry, QualType(SuperType::TY_DYN), QualType(SuperType::TY_INT), noParams, noTemplateTypes, nil<ASTNode*>); | |
| 6675 | 9 | testMain.used = true; // Mark as used to prevent removal | |
| 6676 | 9 | testMain.implicitDefault = true; | |
| 6677 | 9 | testMain.mangleFunctionName = false; | |
| 6678 | |||
| 6679 | // Prepare scope | ||
| 6680 | 9 | this.rootScope.createChildScope(testMain.getScopeName(), ScopeType::FUNC_PROC_BODY, nil<CodeLoc*>); | |
| 6681 | |||
| 6682 | // Generate | ||
| 6683 | 9 | const llvm::Function fct = this.beginImplicitFunction(&testMain, true); | |
| 6684 | 9 | if fct.isNull() { | |
| 6685 | ✗ | return; | |
| 6686 | } | ||
| 6687 | |||
| 6688 | // Prepare result variable | ||
| 6689 | 9 | Vector<llvm::Value> testCaseResults; | |
| 6690 | 9 | testCaseResults.reserve(tests.getSize()); | |
| 6691 | |||
| 6692 | // Print start message | ||
| 6693 | 9 | unsigned long totalTestCount = 0l; | |
| 6694 | 20 | foreach const Vector<const model::Function*>* innerVector : tests { | |
| 6695 | 11 | totalTestCount += innerVector.getSize(); | |
| 6696 | 11 | } | |
| 6697 | 9 | Vector<llvm::Value> allStartArgs; | |
| 6698 | 9 | allStartArgs.pushBack(allStartMsg); | |
| 6699 | 9 | allStartArgs.pushBack(this.getInt32(cast<long>(totalTestCount))); | |
| 6700 | 9 | allStartArgs.pushBack(this.getInt32(tests.getSize())); | |
| 6701 | 9 | this.builder.createCall(printfFct, allStartArgs); | |
| 6702 | |||
| 6703 | // Generate a call to each test function | ||
| 6704 | 20 | foreach const Vector<const model::Function*>* testSuite : tests { | |
| 6705 | // Print test suite prologue | ||
| 6706 | 11 | const model::Function* firstTestFunction = testSuite.front(); | |
| 6707 | 11 | const String& fileName = firstTestFunction.bodyScope.sourceFile.fileName; | |
| 6708 | 11 | const llvm::Value fileNameValue = this.createGlobalStringConst(String("fileName"), fileName, firstTestFunction.getDeclCodeLoc()); | |
| 6709 | 11 | Vector<llvm::Value> fileStartArgs; | |
| 6710 | 11 | fileStartArgs.pushBack(fileStartMsg); | |
| 6711 | 11 | fileStartArgs.pushBack(this.getInt32(testSuite.getSize())); | |
| 6712 | 11 | fileStartArgs.pushBack(fileNameValue); | |
| 6713 | 11 | this.builder.createCall(printfFct, fileStartArgs); | |
| 6714 | |||
| 6715 | 32 | foreach const model::Function* testFunction : *testSuite { | |
| 6716 | 21 | assert testFunction.isNormalFunction(); | |
| 6717 | 21 | assert testFunction.paramList.isEmpty(); | |
| 6718 | |||
| 6719 | // Retrieve attribute list for the test function | ||
| 6720 | 21 | assert testFunction.declNode.isFctOrProcDef(); | |
| 6721 | 21 | const ASTFctDefNode* fctDefNode = downcast<ASTFctDefNode>(testFunction.declNode); | |
| 6722 | 21 | assert fctDefNode.attrs != nil<IVisitable*>; | |
| 6723 | 21 | const ASTTopLevelDefAttrNode* topLevelDefAttrs = downcast<ASTTopLevelDefAttrNode>(fctDefNode.attrs); | |
| 6724 | 21 | const ASTAttrLstNode* attrs = topLevelDefAttrs.attrLst; | |
| 6725 | 21 | const CompileTimeValue testAttrValue = attrs.getAttrValueByName(String(ATTR_TEST)); | |
| 6726 | 21 | assert testAttrValue.getBoolValue(); // The test attribute must be present | |
| 6727 | 21 | bool skipTest = false; | |
| 6728 | 21 | if attrs.hasAttr(String(ATTR_TEST_SKIP)) { | |
| 6729 | 6 | const CompileTimeValue testSkipAttrValue = attrs.getAttrValueByName(String(ATTR_TEST_SKIP)); | |
| 6730 | 6 | skipTest = testSkipAttrValue.getBoolValue(); | |
| 6731 | 6 | } | |
| 6732 | |||
| 6733 | // Prepare test name | ||
| 6734 | 21 | String testName = testFunction.name; | |
| 6735 | 21 | if attrs.hasAttr(String(ATTR_TEST_NAME)) { | |
| 6736 | 2 | const CompileTimeValue testNameAttrValue = attrs.getAttrValueByName(String(ATTR_TEST_NAME)); | |
| 6737 | 2 | const String& testNameAttr = this.resourceManager.compileTimeStringValues.get(testNameAttrValue.getStringValueOffset()); | |
| 6738 | 2 | testName += " ("; | |
| 6739 | 2 | testName += testNameAttr; | |
| 6740 | 2 | testName += ")"; | |
| 6741 | 2 | } | |
| 6742 | |||
| 6743 | // Print test case run message | ||
| 6744 | 21 | const llvm::Value testNameValue = this.createGlobalStringConst(String("testName"), testName, testFunction.getDeclCodeLoc()); | |
| 6745 | 21 | Vector<llvm::Value> runArgs; | |
| 6746 | 21 | runArgs.pushBack(runMsg); | |
| 6747 | 21 | runArgs.pushBack(testNameValue); | |
| 6748 | 21 | this.builder.createCall(printfFct, runArgs); | |
| 6749 | |||
| 6750 | 21 | if skipTest { | |
| 6751 | // Print test case skip message | ||
| 6752 | 2 | Vector<llvm::Value> skipArgs; | |
| 6753 | 2 | skipArgs.pushBack(skippedMsg); | |
| 6754 | 2 | skipArgs.pushBack(testNameValue); | |
| 6755 | 2 | this.builder.createCall(printfFct, skipArgs); | |
| 6756 | 2 | continue; | |
| 6757 | 2 | } | |
| 6758 | |||
| 6759 | // Test function is not defined in the current module -> declare it | ||
| 6760 | 19 | const String mangledName = testFunction.getMangledName(); | |
| 6761 | 19 | const llvm::Function existingTestFct = this.module.getFunction(mangledName.getRaw()); | |
| 6762 | 19 | if existingTestFct.isNull() { | |
| 6763 | 4 | assert testFunction.returnType.is(SuperType::TY_BOOL); | |
| 6764 | 4 | assert testFunction.paramList.isEmpty(); | |
| 6765 | 4 | const Vector<llvm::Type> noParamTypes; | |
| 6766 | 4 | const llvm::Type fctType = llvm::getFunctionType(this.builder.getInt1Ty(), noParamTypes, false); | |
| 6767 | 4 | this.module.getOrInsertFunction(mangledName.getRaw(), fctType); | |
| 6768 | 4 | } | |
| 6769 | |||
| 6770 | // Call test function | ||
| 6771 | 19 | const llvm::Function callee = this.module.getFunction(mangledName.getRaw()); | |
| 6772 | 19 | assert !callee.isNull(); | |
| 6773 | 19 | const Vector<llvm::Value> noArgs; | |
| 6774 | 19 | const llvm::Value testCaseResult = this.builder.createCall(callee, noArgs); | |
| 6775 | 19 | testCaseResults.pushBack(testCaseResult); | |
| 6776 | |||
| 6777 | // Print test case result message | ||
| 6778 | 19 | const llvm::Value message = this.builder.createSelect(testCaseResult, successMsg, errorMsg); | |
| 6779 | 19 | Vector<llvm::Value> resultArgs; | |
| 6780 | 19 | resultArgs.pushBack(message); | |
| 6781 | 19 | resultArgs.pushBack(testNameValue); | |
| 6782 | 19 | this.builder.createCall(printfFct, resultArgs); | |
| 6783 | 21 | } | |
| 6784 | |||
| 6785 | // Print test suite epilogue | ||
| 6786 | 11 | Vector<llvm::Value> fileEndArgs; | |
| 6787 | 11 | fileEndArgs.pushBack(fileEndMsg); | |
| 6788 | 11 | fileEndArgs.pushBack(this.getInt32(testSuite.getSize())); | |
| 6789 | 11 | fileEndArgs.pushBack(fileNameValue); | |
| 6790 | 11 | this.builder.createCall(printfFct, fileEndArgs); | |
| 6791 | 11 | } | |
| 6792 | |||
| 6793 | // Print end message | ||
| 6794 | 9 | Vector<llvm::Value> allEndArgs; | |
| 6795 | 9 | allEndArgs.pushBack(allEndMsg); | |
| 6796 | 9 | allEndArgs.pushBack(this.getInt32(cast<long>(totalTestCount))); | |
| 6797 | 9 | allEndArgs.pushBack(this.getInt32(tests.getSize())); | |
| 6798 | 9 | this.builder.createCall(printfFct, allEndArgs); | |
| 6799 | |||
| 6800 | // Compute overall result | ||
| 6801 | 9 | llvm::Value overallResult = this.builder.getTrue(); | |
| 6802 | 28 | foreach const llvm::Value& testCaseResult : testCaseResults { | |
| 6803 | 19 | overallResult = this.builder.createAnd(overallResult, testCaseResult); | |
| 6804 | 19 | } | |
| 6805 | |||
| 6806 | // Return code must be 0 for success and 1 for failure, so we need to invert the result and zero extend to 32 bit | ||
| 6807 | 9 | const llvm::Value overallResultNegated = this.builder.createNot(overallResult); | |
| 6808 | 9 | const llvm::Value exitCode = this.builder.createZExt(overallResultNegated, this.builder.getInt32Ty()); | |
| 6809 | 9 | this.builder.createRet(exitCode); | |
| 6810 | |||
| 6811 | 9 | this.endImplicitFunction(&testMain, fct, true); | |
| 6812 | 9 | } | |
| 6813 | |||
| 6814 | // ====================================================== Target dependent ======================================================= | ||
| 6815 | |||
| 6816 | 5 | f<String> IRGenerator.getSysCallAsmString(unsigned int numRegs) { | |
| 6817 | 5 | const llvm::Triple& targetTriple = this.cliOptions.targetTriple; | |
| 6818 | 5 | String asmString; | |
| 6819 | |||
| 6820 | // For each OS/architecture we have a mapping of operand index -> register. | ||
| 6821 | // Operand $0 is the result, so the inputs start at $1. | ||
| 6822 | 5 | if targetTriple.isOSDarwin() { | |
| 6823 | ✗ | if targetTriple.isX86_64() { | |
| 6824 | ✗ | const string[7] regs = ["%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9"]; | |
| 6825 | ✗ | for unsigned int i = 0; i < numRegs; i++ { | |
| 6826 | ✗ | asmString += "movq $" + toString(i + 1) + ", " + regs[i] + "\n"; | |
| 6827 | // macOS x86_64 syscall numbers are offset by 0x2000000 (BSD syscall class). "$$" emits a literal '$'. | ||
| 6828 | ✗ | if i == 0 { | |
| 6829 | ✗ | asmString += "addq $$" + toString(33554432) + ", %rax\n"; | |
| 6830 | ✗ | } | |
| 6831 | ✗ | } | |
| 6832 | ✗ | asmString += "syscall\n"; | |
| 6833 | ✗ | } else if targetTriple.isAArch64() { | |
| 6834 | ✗ | const string[7] regs = ["x16", "x0", "x1", "x2", "x3", "x4", "x5"]; | |
| 6835 | ✗ | for unsigned int i = 0; i < numRegs; i++ { | |
| 6836 | ✗ | asmString += String("mov ") + regs[i] + ", $" + toString(i + 1) + "\n"; | |
| 6837 | ✗ | } | |
| 6838 | ✗ | asmString += "svc #0x80\n"; | |
| 6839 | − | } else { // LCOV_EXCL_LINE | |
| 6840 | − | panic(Error("Unsupported macOS target for inline assembly")); // LCOV_EXCL_LINE | |
| 6841 | − | } // LCOV_EXCL_LINE | |
| 6842 | 5 | } else if targetTriple.isOSLinux() { | |
| 6843 | 5 | if targetTriple.isX86_64() { | |
| 6844 | 5 | const string[7] regs = ["%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9"]; | |
| 6845 | 27 | for unsigned int i = 0; i < numRegs; i++ { | |
| 6846 | 22 | asmString += "movq $" + toString(i + 1) + ", " + regs[i] + "\n"; | |
| 6847 | 22 | } | |
| 6848 | 5 | asmString += "syscall\n"; | |
| 6849 | 5 | } else if targetTriple.isX86() { | |
| 6850 | // The operands are bound to their registers via constraints. The sixth arg belongs into ebp, which may be the frame | ||
| 6851 | // pointer. Therefore, it is passed in eax, the syscall number is passed via memory and ebp is restored afterwards. | ||
| 6852 | ✗ | if numRegs == 7 { | |
| 6853 | ✗ | asmString += "pushl $1\npush %ebp\nmov %eax, %ebp\nmov 4(%esp), %eax\nint $$0x80\npop %ebp\nlea 4(%esp), %esp\n"; | |
| 6854 | ✗ | } else { | |
| 6855 | ✗ | asmString += "int $$0x80\n"; | |
| 6856 | ✗ | } | |
| 6857 | ✗ | } else if targetTriple.isAArch64() { | |
| 6858 | ✗ | const string[7] regs = ["x8", "x0", "x1", "x2", "x3", "x4", "x5"]; | |
| 6859 | ✗ | for unsigned int i = 0; i < numRegs; i++ { | |
| 6860 | ✗ | asmString += String("mov ") + regs[i] + ", $" + toString(i + 1) + "\n"; | |
| 6861 | ✗ | } | |
| 6862 | ✗ | asmString += "svc 0\n"; | |
| 6863 | − | } else { // LCOV_EXCL_LINE | |
| 6864 | − | panic(Error("Unsupported Linux target for inline assembly")); // LCOV_EXCL_LINE | |
| 6865 | − | } // LCOV_EXCL_LINE | |
| 6866 | − | } else { // LCOV_EXCL_LINE | |
| 6867 | − | panic(Error("Unsupported target for inline assembly")); // LCOV_EXCL_LINE | |
| 6868 | − | } // LCOV_EXCL_LINE | |
| 6869 | |||
| 6870 | 5 | return asmString; | |
| 6871 | } | ||
| 6872 | |||
| 6873 | 5 | f<String> IRGenerator.getSysCallConstraintString(unsigned int numRegs) { | |
| 6874 | 5 | const llvm::Triple& targetTriple = this.cliOptions.targetTriple; | |
| 6875 | |||
| 6876 | // For each OS/architecture we have a mapping of operand index -> register, plus the register holding the result. | ||
| 6877 | 5 | Vector<String> regs; | |
| 6878 | 5 | String resultReg; | |
| 6879 | 5 | if targetTriple.isOSDarwin() { | |
| 6880 | ✗ | if targetTriple.isX86_64() { | |
| 6881 | ✗ | const string[7] darwinX86_64Regs = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"]; | |
| 6882 | ✗ | for unsigned int i = 0; i < 7; i++ { regs.pushBack(String(darwinX86_64Regs[i])); } | |
| 6883 | ✗ | resultReg = String("rax"); | |
| 6884 | ✗ | } else if targetTriple.isAArch64() { | |
| 6885 | ✗ | const string[7] darwinAArch64Regs = ["x16", "x0", "x1", "x2", "x3", "x4", "x5"]; | |
| 6886 | ✗ | for unsigned int i = 0; i < 7; i++ { regs.pushBack(String(darwinAArch64Regs[i])); } | |
| 6887 | ✗ | resultReg = String("x0"); | |
| 6888 | − | } else { // LCOV_EXCL_LINE | |
| 6889 | − | panic(Error("Unsupported macOS target for inline assembly")); // LCOV_EXCL_LINE | |
| 6890 | − | } // LCOV_EXCL_LINE | |
| 6891 | 5 | } else if targetTriple.isOSLinux() { | |
| 6892 | 5 | if targetTriple.isX86_64() { | |
| 6893 | 5 | const string[7] linuxX86_64Regs = ["rax", "rdi", "rsi", "rdx", "r10", "r8", "r9"]; | |
| 6894 | 40 | for unsigned int i = 0; i < 7; i++ { regs.pushBack(String(linuxX86_64Regs[i])); } | |
| 6895 | 5 | resultReg = String("rax"); | |
| 6896 | 5 | } else if targetTriple.isX86() { | |
| 6897 | // i386 has too few registers to pass the operands in arbitrary ones, so each operand gets its fixed register. | ||
| 6898 | // The syscall number is tied to the result in eax. For six args, see getSysCallAsmString. | ||
| 6899 | ✗ | const string[5] argRegs = ["ebx", "ecx", "edx", "esi", "edi"]; | |
| 6900 | ✗ | String constraints = String("={eax},") + (numRegs == 7 ? "*m" : "0"); | |
| 6901 | ✗ | const unsigned int maxReg = numRegs < 6 ? numRegs : 6; | |
| 6902 | ✗ | for unsigned int i = 1; i < maxReg; i++ { | |
| 6903 | ✗ | constraints += String(",{") + argRegs[i - 1] + "}"; | |
| 6904 | ✗ | } | |
| 6905 | ✗ | if numRegs == 7 { | |
| 6906 | ✗ | constraints += ",0"; | |
| 6907 | ✗ | } | |
| 6908 | ✗ | constraints += ",~{dirflag},~{fpsr},~{flags}"; | |
| 6909 | ✗ | return constraints; | |
| 6910 | ✗ | } else if targetTriple.isAArch64() { | |
| 6911 | ✗ | const string[7] linuxAArch64Regs = ["x8", "x0", "x1", "x2", "x3", "x4", "x5"]; | |
| 6912 | ✗ | for unsigned int i = 0; i < 7; i++ { regs.pushBack(String(linuxAArch64Regs[i])); } | |
| 6913 | ✗ | resultReg = String("x0"); | |
| 6914 | − | } else { // LCOV_EXCL_LINE | |
| 6915 | − | panic(Error("Unsupported Linux target for inline assembly")); // LCOV_EXCL_LINE | |
| 6916 | − | } // LCOV_EXCL_LINE | |
| 6917 | − | } else { // LCOV_EXCL_LINE | |
| 6918 | − | panic(Error("Unsupported target for inline assembly")); // LCOV_EXCL_LINE | |
| 6919 | − | } // LCOV_EXCL_LINE | |
| 6920 | |||
| 6921 | // Generate a comma-separated constraint string: first the result, then the operand constraints, | ||
| 6922 | // then the clobbers of the used registers, then extra clobbers. | ||
| 6923 | // The result is early-clobber, because the asm writes the result register before all inputs are read. | ||
| 6924 | 5 | String constraints = "=&{" + resultReg + "}"; | |
| 6925 | 27 | for unsigned int i = 0; i < numRegs; i++ { | |
| 6926 | 22 | constraints += ",r"; | |
| 6927 | 22 | } | |
| 6928 | 27 | for unsigned int i = 0; i < numRegs; i++ { | |
| 6929 | 22 | const String& reg = regs.get(i); | |
| 6930 | 22 | if reg != resultReg { | |
| 6931 | 17 | constraints += ",~{" + reg + "}"; | |
| 6932 | 17 | } | |
| 6933 | 22 | } | |
| 6934 | 5 | constraints += ",~{dirflag},~{fpsr},~{flags}"; | |
| 6935 | 10 | return constraints; | |
| 6936 | 5 | } | |
| 6937 | |||
| 6938 | // =========================================================== VTables =========================================================== | ||
| 6939 | |||
| 6940 | 1554 | f<llvm::Value> IRGenerator.generateTypeInfoName(StructBase* spiceStruct) { | |
| 6941 | // Resolve mangled type info name and mangled global name | ||
| 6942 | 1554 | const String globalName = mangleTypeInfoName(spiceStruct); | |
| 6943 | |||
| 6944 | // Generate global string constant | ||
| 6945 | 1554 | const String mangledName = mangleTypeInfoValue(spiceStruct.name); | |
| 6946 | 1554 | llvm::Value global = this.module.createGlobalString(mangledName, globalName.getRaw()); | |
| 6947 | |||
| 6948 | // If the output should be comparable, fix alignment to 4 bytes | ||
| 6949 | 1554 | if this.cliOptions.comparableOutput { | |
| 6950 | 1554 | global.setAlignment(4); | |
| 6951 | 1554 | } | |
| 6952 | |||
| 6953 | 1554 | const QualType& entryType = spiceStruct.entry.getQualType(); | |
| 6954 | 1554 | const bool isPublic = entryType.isPublic(); | |
| 6955 | |||
| 6956 | // Set global attributes | ||
| 6957 | 1554 | global.setConstant(true); | |
| 6958 | 1554 | global.setUnnamedAddr(llvm::LLVMUnnamedAddr::NoUnnamedAddr); | |
| 6959 | 1554 | global.setLinkage(this.getVTableLinkageType(isPublic)); | |
| 6960 | // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library) | ||
| 6961 | 1554 | this.attachComdatToSymbol(global, globalName, isPublic); | |
| 6962 | |||
| 6963 | 1554 | spiceStruct.vTableData.typeInfoName = global; | |
| 6964 | 3108 | return global; | |
| 6965 | 1554 | } | |
| 6966 | |||
| 6967 | 1554 | #[ignoreUnusedReturnValue] | |
| 6968 | 1554 | f<llvm::Value> IRGenerator.generateTypeInfo(StructBase* spiceStruct) { | |
| 6969 | // Generate type info name | ||
| 6970 | 1554 | const llvm::Value typeInfoName = this.generateTypeInfoName(spiceStruct); | |
| 6971 | |||
| 6972 | // Generate LLVM type for type info | ||
| 6973 | 1554 | const String mangledName = mangleTypeInfo(spiceStruct); | |
| 6974 | 1554 | const llvm::Type ptrTy = this.builder.getPtrTy(); | |
| 6975 | |||
| 6976 | 1554 | QualTypeList interfaceTypes; | |
| 6977 | 1554 | const QualType& entryType = spiceStruct.entry.getQualType(); | |
| 6978 | 1554 | if entryType.is(SuperType::TY_STRUCT) { | |
| 6979 | unsafe { | ||
| 6980 | // StructBase is the first member of Struct, so both share the same address | ||
| 6981 | 872 | Struct* spiceStructWithInterfaces = cast<Struct*>(spiceStruct); | |
| 6982 | 872 | interfaceTypes = spiceStructWithInterfaces.interfaceTypes; | |
| 6983 | } | ||
| 6984 | 872 | } | |
| 6985 | |||
| 6986 | // Build type info LLVM type | ||
| 6987 | 1554 | Vector<llvm::Type> typeInfoFieldTypes; | |
| 6988 | 1554 | typeInfoFieldTypes.pushBack(ptrTy); | |
| 6989 | 1554 | typeInfoFieldTypes.pushBack(ptrTy); | |
| 6990 | 2260 | for unsigned long i = 0l; i < interfaceTypes.getSize(); i++ { | |
| 6991 | 706 | typeInfoFieldTypes.pushBack(ptrTy); | |
| 6992 | 706 | } | |
| 6993 | 1554 | spiceStruct.vTableData.typeInfoType = llvm::getStructType(*this.context, typeInfoFieldTypes); | |
| 6994 | |||
| 6995 | // Generate type info values | ||
| 6996 | 1554 | llvm::Value typeInfoVTable = llvm::getNullValue(ptrTy); | |
| 6997 | 1554 | if !this.sourceFile.isRttiRT() { | |
| 6998 | // Add external global pointer for TypeInfo vtable | ||
| 6999 | 1394 | assert this.sourceFile.isRuntimeModuleAvailable(RuntimeModule::RTTI_RT); | |
| 7000 | 1394 | const String typeInfoMangledName = mangleVTable(String("TypeInfo")); | |
| 7001 | 1394 | const llvm::Value typeInfoExtPtr = this.module.getOrInsertGlobal(typeInfoMangledName.getRaw(), ptrTy); | |
| 7002 | 1394 | Vector<llvm::Value> indices; | |
| 7003 | 1394 | indices.pushBack(this.getInt64(2l)); | |
| 7004 | 1394 | typeInfoVTable = llvm::getConstInBoundsGEP(ptrTy, typeInfoExtPtr, indices); | |
| 7005 | 1394 | } | |
| 7006 | 1554 | Vector<llvm::Value> fieldValues; | |
| 7007 | 1554 | fieldValues.pushBack(typeInfoVTable); | |
| 7008 | 1554 | fieldValues.pushBack(typeInfoName); | |
| 7009 | 2260 | foreach const QualType& interfaceType : interfaceTypes { | |
| 7010 | 706 | const Interface* spiceInterface = interfaceType.getInterface(nil<ASTNode*>); | |
| 7011 | 706 | assert spiceInterface != nil<const Interface*>; | |
| 7012 | 706 | const String interfaceMangledName = mangleTypeInfo(&spiceInterface.structBase); | |
| 7013 | 706 | const llvm::Value interfaceGlobal = this.module.getOrInsertGlobal(interfaceMangledName.getRaw(), ptrTy); | |
| 7014 | 706 | fieldValues.pushBack(interfaceGlobal); | |
| 7015 | 706 | } | |
| 7016 | 1554 | const llvm::Value typeInfo = llvm::getConstNamedStruct(spiceStruct.vTableData.typeInfoType, fieldValues); | |
| 7017 | |||
| 7018 | // Generate global variable | ||
| 7019 | 1554 | this.module.getOrInsertGlobal(mangledName.getRaw(), spiceStruct.vTableData.typeInfoType); | |
| 7020 | 1554 | llvm::Value global = this.module.getNamedGlobal(mangledName.getRaw()); | |
| 7021 | 1554 | global.setInitializer(typeInfo); | |
| 7022 | |||
| 7023 | 1554 | const bool isPublic = entryType.isPublic(); | |
| 7024 | |||
| 7025 | // Set global attributes | ||
| 7026 | 1554 | global.setConstant(true); | |
| 7027 | 1554 | global.setUnnamedAddr(llvm::LLVMUnnamedAddr::NoUnnamedAddr); | |
| 7028 | 1554 | global.setLinkage(this.getVTableLinkageType(isPublic)); | |
| 7029 | // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library) | ||
| 7030 | 1554 | global.setAlignment(8); | |
| 7031 | 1554 | this.attachComdatToSymbol(global, mangledName, isPublic); | |
| 7032 | |||
| 7033 | 1554 | spiceStruct.vTableData.typeInfo = global; | |
| 7034 | 3108 | return global; | |
| 7035 | 1554 | } | |
| 7036 | |||
| 7037 | 1554 | #[ignoreUnusedReturnValue] | |
| 7038 | 1554 | f<llvm::Value> IRGenerator.generateVTable(StructBase* spiceStruct) { | |
| 7039 | // Generate type info data structures | ||
| 7040 | 1554 | this.generateTypeInfo(spiceStruct); | |
| 7041 | |||
| 7042 | // Generate VTable type | ||
| 7043 | 1554 | spiceStruct.vTableData.vtableType = this.getVTableType(spiceStruct); | |
| 7044 | |||
| 7045 | 1554 | const String mangledName = mangleVTable(spiceStruct); | |
| 7046 | 1554 | this.module.getOrInsertGlobal(mangledName.getRaw(), spiceStruct.vTableData.vtableType); | |
| 7047 | 1554 | llvm::Value global = this.module.getNamedGlobal(mangledName.getRaw()); | |
| 7048 | |||
| 7049 | 1554 | const QualType& entryType = spiceStruct.entry.getQualType(); | |
| 7050 | 1554 | const bool isPublic = entryType.isPublic(); | |
| 7051 | |||
| 7052 | // Set global attributes | ||
| 7053 | 1554 | global.setConstant(true); | |
| 7054 | 1554 | global.setUnnamedAddr(llvm::LLVMUnnamedAddr::GlobalUnnamedAddr); | |
| 7055 | 1554 | global.setLinkage(this.getVTableLinkageType(isPublic)); | |
| 7056 | // ToDo: Set dso_local as soon as the LLVM C API allows it (not for public symbols of a shared library) | ||
| 7057 | 1554 | global.setAlignment(8); | |
| 7058 | 1554 | this.attachComdatToSymbol(global, mangledName, isPublic); | |
| 7059 | |||
| 7060 | 1554 | spiceStruct.vTableData.vtable = global; | |
| 7061 | 3108 | return global; | |
| 7062 | 1554 | } | |
| 7063 | |||
| 7064 | 1554 | p IRGenerator.generateVTableInitializer(const StructBase* spiceStruct) { | |
| 7065 | // Retrieve virtual method count | ||
| 7066 | 1554 | assert spiceStruct.scope != nil<Scope*>; | |
| 7067 | 1554 | const Vector<const model::Function*> virtualMethods = spiceStruct.scope.getVirtualMethods(); | |
| 7068 | |||
| 7069 | // Generate VTable type | ||
| 7070 | 1554 | const llvm::Type ptrTy = this.builder.getPtrTy(); | |
| 7071 | 1554 | assert !spiceStruct.vTableData.vtableType.isNull(); | |
| 7072 | |||
| 7073 | // Generate VTable values | ||
| 7074 | 1554 | Vector<llvm::Value> arrayValues; | |
| 7075 | 1554 | arrayValues.pushBack(llvm::getNullValue(ptrTy)); // nullptr as safety guard | |
| 7076 | 1554 | arrayValues.pushBack(spiceStruct.vTableData.typeInfo); // TypeInfo to identify the type for the VTable | |
| 7077 | 5073 | foreach const model::Function* virtualMethod : virtualMethods { | |
| 7078 | // Methods, that are inherited from an interface default method, are implemented by the default method | ||
| 7079 | 3519 | llvm::Function llvmFunc = virtualMethod.isInheritedDefaultMethod() ? this.getOrDeclareDefaultMethod(virtualMethod.defaultMethod) : this.getLLVMFunction(virtualMethod); | |
| 7080 | 3519 | assert spiceStruct.scope.scopeType == ScopeType::INTERFACE || !llvmFunc.isNull(); | |
| 7081 | // A method may return a struct, where the interface method returns an interface, which the struct implements. Its | ||
| 7082 | // return type does not match the one of the interface method, but it is never called through the VTable: the type | ||
| 7083 | // checker rejects calls through an interface, that return an interface by value (INTERFACE_RETURNED_BY_VALUE) | ||
| 7084 | 3519 | arrayValues.pushBack(!llvmFunc.isNull() ? llvmFunc.asValue() : llvm::getNullValue(ptrTy)); | |
| 7085 | 3519 | } | |
| 7086 | |||
| 7087 | // Generate VTable struct | ||
| 7088 | 1554 | Vector<llvm::Value> fieldValues; | |
| 7089 | 1554 | fieldValues.pushBack(llvm::getConstArray(ptrTy, arrayValues)); | |
| 7090 | 1554 | const llvm::Value initializer = llvm::getConstNamedStruct(spiceStruct.vTableData.vtableType, fieldValues); | |
| 7091 | |||
| 7092 | 1554 | const String mangledName = mangleVTable(spiceStruct); | |
| 7093 | 1554 | llvm::Value global = this.module.getNamedGlobal(mangledName.getRaw()); | |
| 7094 | 1554 | assert !global.isNull(); | |
| 7095 | 1554 | global.setInitializer(initializer); | |
| 7096 | 1554 | } | |
| 7097 | |||
| 7098 | /** | ||
| 7099 | * Get the LLVM function of a function or procedure, that is not a method, and declare it if required, because it is | ||
| 7100 | * defined later, in another source file or externally | ||
| 7101 | * | ||
| 7102 | * @param fct Function or procedure | ||
| 7103 | * @return LLVM function | ||
| 7104 | */ | ||
| 7105 | 64 | f<llvm::Function> IRGenerator.getOrDeclareFunction(const model::Function* fct) { | |
| 7106 | 64 | assert !fct.isMethod(); | |
| 7107 | 64 | const String mangledName = fct.getMangledName(); | |
| 7108 | 64 | const llvm::Function existing = this.module.getFunction(mangledName.getRaw()); | |
| 7109 | 64 | if !existing.isNull() { | |
| 7110 | 120 | return existing; | |
| 7111 | } | ||
| 7112 | |||
| 7113 | 4 | Vector<llvm::Type> paramTypes; | |
| 7114 | 8 | foreach const QualType& paramType : fct.getParamTypes() { | |
| 7115 | 4 | paramTypes.pushBack(paramType.getParamLLVMType(this.sourceFile)); | |
| 7116 | 4 | } | |
| 7117 | 4 | const QualType returnType = fct.isProcedure() ? QualType(SuperType::TY_DYN) : fct.returnType; | |
| 7118 | 4 | const llvm::Type fctType = this.getFunctionType(returnType, paramTypes, fct.isVararg); | |
| 7119 | 4 | this.module.getOrInsertFunction(mangledName.getRaw(), fctType); | |
| 7120 | 4 | llvm::Function llvmFct = this.module.getFunction(mangledName.getRaw()); | |
| 7121 | 4 | const ReturnABIInfo returnABI = this.getReturnABIInfo(returnType); | |
| 7122 | 4 | if returnABI.isIndirect() { | |
| 7123 | ✗ | this.addSRetParamAttrs(llvmFct, returnABI.memoryType); | |
| 7124 | ✗ | } | |
| 7125 | 8 | return llvmFct; | |
| 7126 | 64 | } | |
| 7127 | |||
| 7128 | 8 | f<llvm::Function> IRGenerator.getOrDeclareDefaultMethod(const model::Function* defaultMethod) { | |
| 7129 | 8 | assert defaultMethod.isInterfaceDefaultMethod; | |
| 7130 | // The default method might be defined in another source file or later in this one -> declare it if required | ||
| 7131 | 8 | const String mangledName = defaultMethod.getMangledName(); | |
| 7132 | 8 | const llvm::Function existing = this.module.getFunction(mangledName.getRaw()); | |
| 7133 | 8 | if !existing.isNull() { | |
| 7134 | 16 | return existing; | |
| 7135 | } | ||
| 7136 | |||
| 7137 | ✗ | Vector<llvm::Type> paramTypes; | |
| 7138 | ✗ | paramTypes.pushBack(this.builder.getPtrTy()); // This pointer | |
| 7139 | ✗ | foreach const QualType& paramType : defaultMethod.getParamTypes() { | |
| 7140 | ✗ | paramTypes.pushBack(paramType.getParamLLVMType(this.sourceFile)); | |
| 7141 | ✗ | } | |
| 7142 | ✗ | const llvm::Type fctType = this.getFunctionType(defaultMethod.returnType, paramTypes); | |
| 7143 | ✗ | this.module.getOrInsertFunction(mangledName.getRaw(), fctType); | |
| 7144 | ✗ | llvm::Function fct = this.module.getFunction(mangledName.getRaw()); | |
| 7145 | ✗ | const ReturnABIInfo returnABI = this.getReturnABIInfo(defaultMethod.returnType); | |
| 7146 | ✗ | if returnABI.isIndirect() { | |
| 7147 | ✗ | this.addSRetParamAttrs(fct, returnABI.memoryType); | |
| 7148 | ✗ | } | |
| 7149 | ✗ | return fct; | |
| 7150 | 8 | } | |
| 7151 | |||
| 7152 | 11555 | f<bool> isPublicDefaultMethod(const model::Function* function) { | |
| 7153 | 11555 | return function.isInterfaceDefaultMethod && function.thisType.isPublic(); | |
| 7154 | } | ||
| 7155 | |||
| 7156 | /** | ||
| 7157 | * Check if the given function is emitted with external linkage and therefore visible to the linker under its mangled name. | ||
| 7158 | * Default methods of public interfaces are public, because structs in other source files can inherit them. Test functions | ||
| 7159 | * are public, if there is a test main, because it calls them | ||
| 7160 | * | ||
| 7161 | * @param function Function or procedure | ||
| 7162 | * @param hasTestMain Whether a test main is generated | ||
| 7163 | * @return Exported or not | ||
| 7164 | */ | ||
| 7165 | 101554 | public f<bool> isExportedFunction(const model::Function* function, bool hasTestMain) { | |
| 7166 | 101554 | const QualType& entryType = function.entry.getQualType(); | |
| 7167 | 101554 | if entryType.isPublic() || isPublicDefaultMethod(function) { | |
| 7168 | 89999 | return true; | |
| 7169 | } | ||
| 7170 | 11555 | if function.declNode.kind != ASTNodeKind::FCT_DEF { | |
| 7171 | 8160 | return false; | |
| 7172 | } | ||
| 7173 | // Test functions are only exported, if the test main calls them | ||
| 7174 | 3395 | if !hasTestMain { | |
| 7175 | 3331 | return false; | |
| 7176 | } | ||
| 7177 | 64 | const ASTFctDefNode* fctDefNode = downcast<ASTFctDefNode>(function.declNode); | |
| 7178 | 64 | if fctDefNode.node.attrs == nil<IVisitable*> { | |
| 7179 | 18 | return false; | |
| 7180 | } | ||
| 7181 | 46 | const ASTTopLevelDefAttrNode* attrs = downcast<ASTTopLevelDefAttrNode>(fctDefNode.node.attrs); | |
| 7182 | 46 | const String testAttrName = String(ATTR_TEST); | |
| 7183 | 46 | if !attrs.attrLst.hasAttr(testAttrName) { | |
| 7184 | ✗ | return false; | |
| 7185 | } | ||
| 7186 | 46 | const CompileTimeValue testAttrValue = attrs.attrLst.getAttrValueByName(testAttrName); | |
| 7187 | 92 | return testAttrValue.getBoolValue(); | |
| 7188 | 46 | } | |
| 7189 | |||
| 7190 | 1568 | f<llvm::Type> IRGenerator.getVTableType(const StructBase* spiceStruct) { | |
| 7191 | // Retrieve virtual method count | ||
| 7192 | 1568 | const Vector<const model::Function*> virtualMethods = spiceStruct.scope.getVirtualMethods(); | |
| 7193 | 1568 | const unsigned long arrayElementCount = virtualMethods.getSize() + 2l; // +2 for nullptr and TypeInfo | |
| 7194 | |||
| 7195 | 1568 | const llvm::Type vtableArrayTy = llvm::getArrayType(this.builder.getPtrTy(), arrayElementCount); | |
| 7196 | 1568 | Vector<llvm::Type> vtableFieldTypes; | |
| 7197 | 1568 | vtableFieldTypes.pushBack(vtableArrayTy); | |
| 7198 | 3136 | return llvm::getStructType(*this.context, vtableFieldTypes, false); | |
| 7199 | 1568 | } | |
| 7200 | |||
| 7201 | 14 | f<llvm::Value> IRGenerator.getVTableAddressPoint(const StructBase* spiceStruct) { | |
| 7202 | // Look up the VTable by name in the current module instead of using vTableData.vtable. The latter belongs to the | ||
| 7203 | // module of the source file that defines the struct, so for an imported struct a declaration is inserted instead. | ||
| 7204 | 14 | const llvm::Type vtableType = this.getVTableType(spiceStruct); | |
| 7205 | 14 | const String mangledName = mangleVTable(spiceStruct); | |
| 7206 | 14 | const llvm::Value vtable = this.module.getOrInsertGlobal(mangledName.getRaw(), vtableType); | |
| 7207 | |||
| 7208 | // The address point of the VTable is behind the nullptr and the TypeInfo | ||
| 7209 | 14 | Vector<llvm::Value> indices; | |
| 7210 | 14 | indices.pushBack(this.getInt64(0l)); | |
| 7211 | 14 | indices.pushBack(this.getInt32(0l)); | |
| 7212 | 14 | indices.pushBack(this.getInt32(2l)); | |
| 7213 | 28 | return llvm::getConstInBoundsGEP(vtableType, vtable, indices); | |
| 7214 | 14 | } | |
| 7215 | |||
| 7216 | // ======================================================= Instrumentation ======================================================= | ||
| 7217 | |||
| 7218 | 44014 | p IRGenerator.enableFunctionInstrumentation(llvm::Function function) { | |
| 7219 | 44014 | switch this.cliOptions.instrumentation.sanitizer { | |
| 7220 | − | case Sanitizer::NONE: {} // LCOV_EXCL_LINE | |
| 7221 | 1336 | case Sanitizer::ADDRESS: { function.addFnAttr("sanitize_address"); } | |
| 7222 | 184 | case Sanitizer::THREAD: { function.addFnAttr("sanitize_thread"); } | |
| 7223 | 4 | case Sanitizer::MEMORY: { function.addFnAttr("sanitize_memory"); } | |
| 7224 | 4 | case Sanitizer::TYPE: { function.addFnAttr("sanitize_type"); } | |
| 7225 | − | default: { panic(Error("Unhandled sanitizer type")); } // LCOV_EXCL_LINE | |
| 7226 | } | ||
| 7227 | 44014 | } | |
| 7228 |