1 /// Copyright: Copyright (c) 2017-2019 Andrey Penechko. 2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 /// Authors: Andrey Penechko. 4 module vox.fe.ast.decl.func; 5 6 import vox.all; 7 8 enum FuncDeclFlags : ushort 9 { 10 isInline = AstFlags.userFlag << 0, 11 isBuiltin = AstFlags.userFlag << 1, 12 } 13 14 /// Refers to a function inside a module 15 struct FunctionIndex 16 { 17 /// Index into ModuleDeclNode.functions 18 uint functionIndex; 19 ModuleIndex moduleIndex; 20 } 21 22 struct FunctionBackendData 23 { 24 /// Machine-independent IR 25 AstIndex irData; // IrFunction 26 AstIndex optimizedIrData; // IrFunction 27 AstIndex loweredIrData; // IrFunction 28 /// Machine-level IR 29 AstIndex lirData; // IrFunction 30 /// 31 LinkIndex objectSymIndex; 32 } 33 34 @(AstType.decl_function) 35 struct FunctionDeclNode { 36 mixin AstNodeData!(AstType.decl_function); 37 AstIndex _module; 38 // scopes are in a hierarchy as: 39 // ownerScope 40 // + parameterScope 41 // + body scope 42 ScopeIndex parameterScope; 43 AstIndex signature; // FunctionSignatureNode 44 AstIndex block_stmt; // null if external 45 Identifier id; 46 FunctionBackendData backendData; 47 48 bool isInline() { return cast(bool)(flags & FuncDeclFlags.isInline); } 49 bool isBuiltin() { return cast(bool)(flags & FuncDeclFlags.isBuiltin); } 50 bool isCtfeOnly(CompilationContext* c) { 51 return signature.get!FunctionSignatureNode(c).isCtfeOnly; 52 } 53 /// External functions have no body 54 bool isExternal() { return block_stmt.isUndefined; } 55 56 this(TokenIndex loc, AstIndex _module, ScopeIndex parameterScope, AstIndex signature, Identifier id) 57 { 58 this.loc = loc; 59 this.astType = AstType.decl_function; 60 this.flags = 0; 61 this._module = _module; 62 this.parameterScope = parameterScope; 63 this.signature = signature; 64 this.id = id; 65 } 66 67 IrIndex getIrIndex(CompilationContext* c) { 68 // IR index of the function depends on full IR type of the signature 69 signature.get!FunctionSignatureNode(c).getIrType(c); 70 71 AstIndex index = get_ast_index(&this, c); 72 return IrIndex(index.storageIndex, IrValueKind.func); 73 } 74 } 75 76 void print_func(FunctionDeclNode* node, ref AstPrintState state) 77 { 78 state.print("FUNC ", state.context.idString(node.id), 79 node.isBuiltin ? " #builtin" : null, 80 node.isInline ? " #inline" : null); 81 print_ast(node.signature, state); 82 if (node.block_stmt) print_ast(node.block_stmt, state); 83 } 84 85 void post_clone_func(FunctionDeclNode* node, ref CloneState state) 86 { 87 state.fixScope(node.parameterScope); 88 state.fixAstIndex(node._module); 89 state.fixAstIndex(node.signature); 90 state.fixAstIndex(node.block_stmt); 91 } 92 93 void name_register_self_func(AstIndex nodeIndex, FunctionDeclNode* node, ref NameRegisterState state) { 94 auto c = state.context; 95 node.state = AstNodeState.name_register_self; 96 97 // Template instance shouldn't register itself 98 // They are discovered with template instantiation syntax 99 // Instance is wrapped in special scope, which shouldn't have function name inserted, only template args 100 if (!node.isTemplateInstance) 101 { 102 // can't be done at parse time because of conditional compilation 103 node.parameterScope.get_scope(c).parentScope.insert_scope(node.id, nodeIndex, c); 104 } 105 auto mod = node._module.get!ModuleDeclNode(c); 106 mod.addFunction(nodeIndex, c); 107 108 // Create link object 109 { 110 if (node.isExternal) 111 { 112 auto sig = node.signature.get!FunctionSignatureNode(c); 113 if (!sig.hasExternAttrib) { 114 c.error(node.loc, "External function `%s` must be annotated with @extern attribute", c.idString(node.id)); 115 } else { 116 auto attrib = sig.getExternAttrib(c).as!BuiltinAttribNode(c); 117 118 final switch(cast(BuiltinAttribSubType)attrib.subType) { 119 case BuiltinAttribSubType.extern_syscall: 120 // Allowed if it is marked with @extern(syscall) 121 // noop, syscall instruction will be generated 122 break; 123 124 case BuiltinAttribSubType.extern_module: 125 Identifier modId = Identifier(attrib.data); 126 Identifier symId = node.id; 127 auto externalId = ExternalSymbolId(modId, symId); 128 129 final switch(c.buildType) { 130 case BuildType.jit: 131 // When JIT-compiling, host can provide a set of modules that define external functions 132 LinkIndex symbolIndex = c.externalSymbols.get(externalId); 133 134 if (!symbolIndex.isDefined) { 135 if (c.externalModules.get(modId).isDefined) 136 c.error(node.loc, "Cannot find external symbol `%s` in host module `%s`", symId.pr(c), modId.pr(c)); 137 else 138 c.error(node.loc, "Cannot find external symbol `%s` in host module `%s`. No such module defined", symId.pr(c), modId.pr(c)); 139 break; 140 } 141 142 // TODO: check that parameters match 143 node.backendData.objectSymIndex = symbolIndex; 144 break; 145 146 case BuildType.exe: 147 // When compiling exe, external symbol will point to a shared library 148 LinkIndex symbolIndex = c.externalSymbols.get(externalId); 149 150 if (!symbolIndex.isDefined) { 151 // Will create a new module if not found 152 // Dll symbols will be imported from this module 153 LinkIndex moduleIndex = c.getOrCreateExternalModule(modId, ObjectModuleKind.isImported); 154 // Create symbol if it doesn't exist 155 symbolIndex = c.addDllModuleSymbol(moduleIndex, symId); 156 } 157 158 node.backendData.objectSymIndex = symbolIndex; 159 break; 160 } 161 break; 162 } 163 } 164 } 165 else 166 { 167 ObjectSymbol sym = { 168 kind : ObjectSymbolKind.isLocal, 169 sectionIndex : c.builtinSections[ObjectSectionType.code], 170 moduleIndex : mod.objectSymIndex, 171 alignmentPower : 0, 172 id : node.id, 173 }; 174 node.backendData.objectSymIndex = c.objSymTab.addSymbol(sym); 175 } 176 } 177 178 node.state = AstNodeState.name_register_self_done; 179 } 180 181 void name_register_nested_func(AstIndex nodeIndex, FunctionDeclNode* node, ref NameRegisterState state) { 182 node.state = AstNodeState.name_register_nested; 183 require_name_register(node.signature, state); 184 if (node.block_stmt) 185 { 186 // TODO: we don't need to register parameters on function without body 187 require_name_register(node.block_stmt, state); 188 } 189 node.state = AstNodeState.name_register_nested_done; 190 } 191 192 void name_resolve_func(FunctionDeclNode* node, ref NameResolveState state) { 193 node.state = AstNodeState.name_resolve; 194 // TODO: parameters don't need to see each other (including default param value expr) 195 require_name_resolve(node.signature, state); 196 if (node.block_stmt) 197 { 198 // TODO: we don't need to register parameters on function without body 199 require_name_resolve(node.block_stmt, state); 200 } 201 node.state = AstNodeState.name_resolve_done; 202 } 203 204 void type_check_func(FunctionDeclNode* node, ref TypeCheckState state) 205 { 206 CompilationContext* c = state.context; 207 208 node.state = AstNodeState.type_check; 209 210 require_type_check(node.signature, state); 211 if (node.block_stmt) require_type_check(node.block_stmt, state); 212 213 node.state = AstNodeState.type_check_done; 214 } 215 216 // ModuleDeclNode.functions are processed sequentially. No nesting can occur. 217 void ir_gen_function(ref IrGenState gen, FunctionDeclNode* f) 218 { 219 if (f.state >= AstNodeState.ir_gen_done) return; // already generated 220 221 CompilationContext* c = gen.context; 222 IrBuilder* builder = &gen.builder; 223 224 c.currentFunction = f; 225 scope(exit) c.currentFunction = null; 226 227 228 f.state = AstNodeState.ir_gen; 229 scope(exit) f.state = AstNodeState.ir_gen_done; 230 231 232 // Do not generate body for the external functions 233 if (f.isExternal) return; 234 235 // function type must be generated even if it is external 236 auto signature = f.signature.get!FunctionSignatureNode(c); 237 IrIndex type = f.signature.gen_ir_type(c); 238 239 gen.fun = f; 240 scope(exit) gen.fun = null; 241 242 // create new function 243 AstIndex irIndex = c.appendAst!IrFunction; 244 f.backendData.irData = irIndex; 245 gen.ir = c.getAst!IrFunction(irIndex); 246 IrFunction* ir = gen.ir; 247 ir.name = f.id; 248 249 ir.type = type; 250 ir.instructionSet = IrInstructionSet.ir; 251 252 builder.begin(ir, c); 253 254 foreach (AstIndex param; signature.parameters) 255 { 256 IrLabel dummy; 257 ir_gen_stmt(gen, param, ir.entryBasicBlock, dummy); 258 } 259 260 builder.addJump(ir.entryBasicBlock); 261 262 IrIndex body_block = builder.addBasicBlock(); 263 builder.addBlockTarget(ir.entryBasicBlock, body_block); 264 builder.sealBlock(body_block); 265 266 // label at the end of body 267 IrLabel bodyExitLabel = IrLabel(body_block); 268 269 // compile body 270 ir_gen_stmt(gen, f.block_stmt, body_block, bodyExitLabel); 271 272 IrIndex currentBlock = bodyExitLabel.blockIndex; 273 // In case new block was created, no new predecessors will be added 274 builder.sealBlock(currentBlock); 275 276 if (!signature.returnType.isVoidType(c)) 277 { 278 // currentBlock must be finished with retVal 279 if (!ir.getBlock(currentBlock).isFinished) 280 { 281 c.unrecoverable_error(f.loc, 282 "function `%s` has no return statement, but is expected to return a value of type %s", 283 c.idString(f.id), signature.returnType.typeName(c)); 284 } 285 286 auto exitBlock = ir.getBlock(ir.exitBasicBlock); 287 if (exitBlock.predecessors.empty) 288 { 289 // control flow doesn't reach exit block. Remove return var phi function. 290 removeAllPhis(*exitBlock); 291 removeAllInstrs(*exitBlock); 292 builder.emitInstr!(IrOpcode.unreachable)(ir.exitBasicBlock); 293 } 294 } 295 else 296 { 297 // currentBlock must be finished with ret or, not finished 298 if (!ir.getBlock(currentBlock).isFinished) 299 { 300 builder.addReturn(currentBlock); 301 } 302 } 303 304 //dumpFunction(c, ir, "IR gen end"); 305 306 // all blocks with return (exit's predecessors) already connected, seal exit block 307 builder.sealBlock(ir.exitBasicBlock); 308 309 builder.finalizeIr; 310 311 if (c.validateIr) validateIrFunction(c, ir, "IR gen"); 312 if (c.printIr && c.printDumpOf(f)) dumpFunction(c, ir, "IR gen"); 313 }