1 /** 2 Copyright: Copyright (c) 2017-2020 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module vox.fe.passes.ast_to_ir; 7 8 import std.stdio; 9 import vox.all; 10 11 12 void pass_ir_gen(ref CompilationContext ctx, CompilePassPerModule[] subPasses) { 13 IrGenState state = { 14 context : &ctx 15 }; 16 foreach (ref SourceFileInfo file; ctx.files.data) { 17 ir_gen_module_globals(state, file.mod); 18 } 19 foreach (ref SourceFileInfo file; ctx.files.data) { 20 ir_gen_module_func(state, file.mod); 21 } 22 } 23 24 enum MAX_GEP_INDICES = 255; 25 struct IrGenState 26 { 27 CompilationContext* context; 28 alias context this; 29 30 IrBuilder builder; 31 IrFunction* ir; 32 FunctionDeclNode* fun; 33 34 IrIndex[MAX_GEP_INDICES+2] gepBuf = void; // 2 is extra parameters to GEP instruction 35 36 IrLabel* currentLoopHeader; 37 IrLabel* currentLoopEnd; 38 } 39 40 void ir_gen_decl(ref IrGenState gen, AstIndex nodeIndex) 41 { 42 CompilationContext* c = gen.context; 43 AstNode* n = c.getAstNode(nodeIndex); 44 switch(n.astType) with(AstType) 45 { 46 case decl_enum, decl_enum_member, decl_function, decl_struct, decl_import, decl_alias, decl_template, decl_static_assert: break; 47 case decl_var: ir_gen_decl_var(c, cast(VariableDeclNode*)n); break; 48 default: 49 c.internal_error(n.loc, "ir_gen_decl %s in %s state", n.astType, n.state); 50 } 51 } 52 53 void ir_gen_stmt(ref IrGenState gen, AstIndex astIndex, IrIndex curBlock, ref IrLabel nextStmt) 54 { 55 CompilationContext* c = gen.context; 56 AstNode* n = c.getAstNode(astIndex); 57 switch(n.astType) with(AstType) 58 { 59 case stmt_block: ir_gen_block (gen, curBlock, nextStmt, cast(BlockStmtNode*)n); break; 60 case stmt_if: ir_gen_if (gen, curBlock, nextStmt, cast(IfStmtNode*)n); break; 61 case stmt_while: ir_gen_while (gen, curBlock, nextStmt, cast(WhileStmtNode*)n); break; 62 case stmt_do_while: ir_gen_do (gen, curBlock, nextStmt, cast(DoWhileStmtNode*)n); break; 63 case stmt_for: ir_gen_for (gen, curBlock, nextStmt, cast(ForStmtNode*)n); break; 64 case stmt_switch: ir_gen_switch (gen, curBlock, nextStmt, cast(SwitchStmtNode*)n); break; 65 case stmt_return: ir_gen_return (gen, curBlock, nextStmt, cast(ReturnStmtNode*)n); break; 66 case stmt_break: ir_gen_break (gen, curBlock, nextStmt, cast(BreakStmtNode*)n); break; 67 case stmt_continue: ir_gen_continue(gen, curBlock, nextStmt, cast(ContinueStmtNode*)n); break; 68 69 // expression statement, must have side effect 70 case expr_call: ir_gen_call(gen, curBlock, nextStmt, cast(CallExprNode*)n); break; 71 case expr_bin_op: ir_gen_expr_binary_op(gen, curBlock, nextStmt, cast(BinaryExprNode*)n); break; 72 case expr_un_op: ir_gen_expr_unary_op(gen, curBlock, nextStmt, cast(UnaryExprNode*)n); break; 73 // should be catched in semantic check, since they have no side effect 74 case expr_member: 75 case expr_name_use: 76 case expr_index: 77 case expr_slice: 78 case expr_type_conv: 79 case literal_int: 80 case literal_float: 81 case literal_string: 82 case literal_null: 83 case literal_bool: 84 case decl_template_param: 85 c.internal_error(n.loc, "stmt %s in %s state", n.astType, n.state); 86 87 // declaration statement 88 case decl_alias: 89 case decl_enum: 90 case decl_enum_member: 91 case decl_function: 92 case decl_struct: 93 case decl_template: 94 case decl_static_assert: 95 case decl_import: gen.builder.addJumpToLabel(curBlock, nextStmt); break; 96 case decl_var: ir_gen_local_var(gen, curBlock, nextStmt, cast(VariableDeclNode*)n); break; 97 98 default: c.internal_error("%s", n.astType); 99 } 100 } 101 102 ExprValue ir_gen_expr(ref IrGenState gen, AstIndex astIndex, IrIndex curBlock, ref IrLabel nextStmt) 103 { 104 CompilationContext* c = gen.context; 105 AstNode* n = gen.getAstNode(astIndex); 106 switch(n.astType) with(AstType) 107 { 108 case expr_name_use: return ir_gen_name_use(gen, curBlock, nextStmt, cast(NameUseExprNode*)n); 109 case expr_member: return ir_gen_member(gen, curBlock, nextStmt, cast(MemberExprNode*)n); 110 case expr_call: return ir_gen_call(gen, curBlock, nextStmt, cast(CallExprNode*)n); 111 case expr_index: return ir_gen_index(gen, curBlock, nextStmt, cast(IndexExprNode*)n); 112 case expr_slice: return ir_gen_expr_slice(gen, curBlock, nextStmt, cast(SliceExprNode*)n); 113 case expr_bin_op: return ir_gen_expr_binary_op(gen, curBlock, nextStmt, cast(BinaryExprNode*)n); 114 case expr_un_op: return ir_gen_expr_unary_op(gen, curBlock, nextStmt, cast(UnaryExprNode*)n); 115 case expr_type_conv: return ir_gen_expr_type_conv(gen, curBlock, nextStmt, cast(TypeConvExprNode*)n); 116 case literal_int: { 117 IrIndex irValue = ir_gen_literal_int(gen.context, cast(IntLiteralExprNode*)n); 118 gen.builder.addJumpToLabel(curBlock, nextStmt); 119 return ExprValue(irValue); 120 } 121 case literal_float: { 122 IrIndex irValue = ir_gen_literal_float(gen.context, cast(FloatLiteralExprNode*)n); 123 gen.builder.addJumpToLabel(curBlock, nextStmt); 124 return ExprValue(irValue); 125 } 126 case literal_string: { 127 IrIndex irValue = ir_gen_literal_string(gen.context, cast(StringLiteralExprNode*)n); 128 gen.builder.addJumpToLabel(curBlock, nextStmt); 129 return ExprValue(irValue); 130 } 131 case literal_null: { 132 IrIndex irValue = ir_gen_literal_null(gen.context, cast(NullLiteralExprNode*)n); 133 gen.builder.addJumpToLabel(curBlock, nextStmt); 134 return ExprValue(irValue); 135 } 136 case literal_bool: { 137 IrIndex irValue = ir_gen_literal_bool(gen.context, cast(BoolLiteralExprNode*)n); 138 gen.builder.addJumpToLabel(curBlock, nextStmt); 139 return ExprValue(irValue); 140 } 141 case literal_special: { 142 IrIndex irValue = ir_gen_literal_special(gen.context, cast(SpecialLiteralExprNode*)n); 143 gen.builder.addJumpToLabel(curBlock, nextStmt); 144 return ExprValue(irValue); 145 } 146 case decl_struct, type_basic, type_ptr, type_slice, type_static_array: { 147 IrIndex irValue = gen.context.constants.add(makeIrType(IrBasicType.i32), astIndex.storageIndex); 148 gen.builder.addJumpToLabel(curBlock, nextStmt); 149 return ExprValue(irValue); 150 } 151 case decl_enum_member: { 152 gen.builder.addJumpToLabel(curBlock, nextStmt); 153 return ExprValue(n.as!EnumMemberDecl(c).gen_init_value_enum_member(c)); 154 } 155 case decl_var: { 156 auto v = n.as!VariableDeclNode(c); 157 if (v.isGlobal) 158 { 159 ir_gen_decl_var(c, v); 160 } 161 c.assertf(v.irValue.irValue.isDefined, "Value is undefined"); 162 ExprValue result = v.irValue; 163 gen.builder.addJumpToLabel(curBlock, nextStmt); 164 return result; 165 } 166 case decl_function: { 167 gen.builder.addJumpToLabel(curBlock, nextStmt); 168 return ExprValue(n.as!FunctionDeclNode(c).getIrIndex(c)); 169 } 170 default: 171 c.internal_error(n.loc, "Expected expression, not %s", n.astType); 172 } 173 } 174 175 void ir_gen_branch(ref IrGenState gen, AstIndex astIndex, IrIndex curBlock, ref IrLabel trueExit, ref IrLabel falseExit) 176 { 177 CompilationContext* c = gen.context; 178 AstNode* n = gen.getAstNode(astIndex); 179 switch(n.astType) with(AstType) 180 { 181 case literal_int, literal_float, literal_string, expr_index, expr_slice: // TODO: expr_index may return bool 182 gen.internal_error("Trying to branch directly on %s, must be wrapped in convertion to bool", n.astType); 183 case expr_bin_op: ir_gen_branch_binary_op (gen, curBlock, trueExit, falseExit, cast(BinaryExprNode*)n); break; 184 case expr_type_conv: ir_gen_branch_type_conv (gen, curBlock, trueExit, falseExit, cast(TypeConvExprNode*)n); break; 185 case expr_un_op: ir_gen_branch_unary_op (gen, curBlock, trueExit, falseExit, cast(UnaryExprNode*)n); break; 186 case literal_bool: ir_gen_branch_literal_bool(gen, curBlock, trueExit, falseExit, cast(BoolLiteralExprNode*)n); break; 187 case expr_name_use, expr_call, expr_member: 188 IrLabel afterExpr = IrLabel(curBlock); 189 ExprValue lval = ir_gen_expr(gen, astIndex, curBlock, afterExpr); 190 curBlock = afterExpr.blockIndex; 191 IrIndex rval = lval.rvalue(gen, n.loc, curBlock); 192 addUnaryBranch(gen, rval, curBlock, trueExit, falseExit); 193 break; 194 195 default: gen.internal_error(n.loc, "Expected expression, not %s", n.astType); 196 } 197 } 198 199 void genBlock(ref IrGenState gen, AstNode* parent, ref AstNodes statements, IrIndex currentBlock, ref IrLabel nextStmt) 200 { 201 foreach (i, AstIndex stmt; statements) 202 { 203 // if not the last statement of block 204 if (i < statements.length - 1) 205 { 206 // nested statement will jump here at its end 207 IrLabel afterStmt = IrLabel(currentBlock); 208 209 // compile nested statement 210 ir_gen_stmt(gen, stmt, currentBlock, afterStmt); 211 212 if (afterStmt.numPredecessors == 0) 213 { 214 // Nested statement never returns here 215 // Skip the rest of block statements 216 return; 217 } 218 219 // If statement returned, get the new current block, 220 // as it could have splitted the CFG and created a new block 221 currentBlock = afterStmt.blockIndex; 222 // Also seal it, since no other block can jump here 223 gen.builder.sealBlock(currentBlock); 224 } 225 else // last statement 226 { 227 // let last statement exit straight to outer scope 228 ir_gen_stmt(gen, stmt, currentBlock, nextStmt); 229 230 // if statement hasn't returned here, let outer scope handle this 231 // the body exit is handled by function decl code 232 } 233 } 234 235 if (statements.length == 0) 236 gen.builder.addJumpToLabel(currentBlock, nextStmt); 237 } 238 239 IrIndex makeBoolValue(ref IrGenState gen, ExpressionNode* n, IrIndex currentBlock, ref IrLabel nextStmt) 240 { 241 CompilationContext* c = gen.context; 242 IrBuilder* builder = &gen.builder; 243 244 IrLabel trueLabel = IrLabel(currentBlock); 245 IrLabel falseLabel = IrLabel(currentBlock); 246 IrLabel nextLabel = IrLabel(currentBlock); 247 IrIndex nextBlock; 248 ir_gen_branch(gen, c.getAstNodeIndex(n), currentBlock, trueLabel, falseLabel); 249 250 IrIndex value; 251 IrIndex irType = n.type.gen_ir_type(c); 252 253 if (trueLabel.numPredecessors != 0) 254 { 255 IrIndex trueBlock = trueLabel.blockIndex; 256 builder.sealBlock(trueBlock); 257 builder.addJumpToLabel(trueBlock, nextLabel); 258 259 if (falseLabel.numPredecessors != 0) // both blocks exist 260 { 261 IrIndex falseBlock = falseLabel.blockIndex; 262 builder.sealBlock(falseBlock); 263 builder.addJumpToLabel(falseBlock, nextLabel); 264 265 nextBlock = nextLabel.blockIndex; 266 builder.sealBlock(nextBlock); 267 268 IrIndex phiIndex = builder.addPhi(nextBlock, irType, IrIndex.init); 269 IrIndex trueValue = c.constants.add(irType, 1); 270 builder.addPhiArg(phiIndex, trueValue); 271 IrIndex falseValue = c.constants.addZeroConstant(irType); 272 builder.addPhiArg(phiIndex, falseValue); 273 value = builder.ir.getPhi(phiIndex).result; 274 } 275 else // only true block exists 276 { 277 nextBlock = trueBlock; 278 value = c.constants.add(irType, 1); 279 } 280 } 281 else if (falseLabel.numPredecessors != 0) // only false block exists 282 { 283 nextBlock = falseLabel.blockIndex; 284 builder.sealBlock(nextBlock); 285 286 value = c.constants.addZeroConstant(irType); 287 } 288 289 builder.addJumpToLabel(nextBlock, nextStmt); 290 291 return value; 292 } 293 294 void addUnaryBranch(ref IrGenState gen, IrIndex value, IrIndex currentBlock, ref IrLabel trueExit, ref IrLabel falseExit) 295 { 296 CompilationContext* c = gen.context; 297 if (value.isSimpleConstant) 298 { 299 long conValue = c.constants.get(value).i64; 300 if (conValue != 0) 301 gen.builder.addJumpToLabel(currentBlock, trueExit); 302 else 303 gen.builder.addJumpToLabel(currentBlock, falseExit); 304 return; 305 } 306 307 IrArgSize argSize = sizeToIrArgSize(c.types.typeSize(gen.ir.getValueType(c, value)), c); 308 gen.builder.addUnaryBranch(currentBlock, IrUnaryCondition.not_zero, argSize, value, trueExit, falseExit); 309 } 310 311 312 enum ExprValueKind : ubyte { 313 // irValue is variable (isLvalue=true), constant or vreg (isLvalue=false) being used directly 314 value, 315 // it is data in source language, but prt to data in IR 316 // for example 1st parameter of function that is passed as pointer to struct in RCX in win64 CC 317 // irValue is pointer value (vreg, global, stack slot) 318 ptr_to_data, 319 // it is data in source language, but prt to ptr to data in IR 320 // for example 5th parameter of function that is passed as pointer to struct on stack in win64 CC 321 ptr_to_ptr_to_data, 322 // irValue is variable (isLvalue=true), constant or vreg (isLvalue=false) 323 // variable can contain aggregate value as well as pointer to aggregate 324 // numIndices indicates number of gepBuf indices being used 325 struct_sub_index, 326 } 327 328 enum IsLvalue : bool { 329 no = false, 330 yes = true, 331 } 332 333 /// Lvalue means that value is stored in global, stack slot or variable. 334 /// 335 struct ExprValue 336 { 337 // IR value 338 IrIndex irValue; 339 // Describes irValue 340 private ExprValueKind kind = ExprValueKind.value; 341 // true if can be assigned or address taken 342 private IsLvalue isLvalue = IsLvalue.no; 343 // 344 private Array!IrIndex indices; 345 // used as offset into aggregate values 346 //IrIndex offset; 347 348 ExprValue addrOf(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock) 349 { 350 ExprValue res = this; 351 res.isLvalue = IsLvalue.no; 352 return res; 353 } 354 355 ExprValue deref(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock) 356 { 357 switch (kind) { 358 case ExprValueKind.value: 359 return ExprValue(irValue, ExprValueKind.ptr_to_data, IsLvalue.yes); 360 case ExprValueKind.ptr_to_data: 361 return ExprValue(irValue, ExprValueKind.ptr_to_ptr_to_data, IsLvalue.yes); 362 default: 363 gen.context.internal_error(loc, "%s", kind); 364 } 365 } 366 367 /// loads value from pointer. pointer must be an rvalue 368 IrIndex load(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock) 369 { 370 CompilationContext* c = gen.context; 371 372 IrIndex source = this.irValue; 373 374 switch (source.kind) with(IrValueKind) 375 { 376 case variable: 377 // it's variable holding pointer 378 source = gen.builder.readVariable(currentBlock, source); 379 goto case; 380 381 case stackSlot, global, virtualRegister: 382 // those are already a pointer 383 IrIndex resultType = c.types.getPointerBaseType(gen.ir.getValueType(c, source)); 384 ExtraInstrArgs extra = {type : resultType}; 385 if (resultType.isTypeAggregate) 386 return gen.builder.emitInstr!(IrOpcode.load_aggregate)(currentBlock, extra, source).result; 387 else 388 { 389 extra.argSize = resultType.getTypeArgSize(c); 390 return gen.builder.emitInstr!(IrOpcode.load)(currentBlock, extra, source).result; 391 } 392 393 default: 394 c.internal_error(loc, "Cannot load from %s", source.kind); 395 } 396 } 397 398 /// returns value as intended by frontend 399 IrIndex rvalue(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock) 400 { 401 CompilationContext* c = gen.context; 402 ExprValue source = this; 403 //writefln("getRvalue %s", source); 404 switch (source.kind) with(ExprValueKind) 405 { 406 case value: 407 switch (source.irValue.kind) with(IrValueKind) 408 { 409 case variable: return gen.builder.readVariable(currentBlock, source.irValue); 410 default: return source.irValue; 411 } 412 case ptr_to_data: 413 if (source.isLvalue) { 414 return source.load(gen, loc, currentBlock); 415 } else { 416 if (source.irValue.isVariable) { 417 return gen.builder.readVariable(currentBlock, source.irValue); 418 } 419 return source.irValue; 420 } 421 case ptr_to_ptr_to_data: 422 IrIndex ptr = source.load(gen, loc, currentBlock); 423 return ExprValue(ptr).load(gen, loc, currentBlock); 424 case struct_sub_index: 425 IrIndex aggr = source.irValue; 426 if (aggr.isVariable) { 427 aggr = gen.builder.readVariable(currentBlock, aggr); 428 } 429 430 IrIndex aggrType = gen.ir.getValueType(c, aggr); 431 switch (aggrType.typeKind) { 432 case IrTypeKind.pointer: 433 IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32)); 434 IrIndex ptr = buildGEP(gen, loc, currentBlock, aggr, ZERO, source.indices[]); 435 if (source.isLvalue) { 436 return ExprValue(ptr).load(gen, loc, currentBlock); 437 } else { 438 return ptr; 439 } 440 case IrTypeKind.array: 441 case IrTypeKind.struct_t: 442 IrIndex memberType = c.types.getAggregateMember(aggrType, c, source.indices[]).type; 443 ExtraInstrArgs extra = { type : memberType }; 444 IrIndex[] args = gen.gepBuf[0..source.indices.length+1]; 445 args[0] = aggr; 446 args[1..$] = source.indices[]; 447 return gen.builder.emitInstr!(IrOpcode.get_element)(currentBlock, extra, args).result; 448 default: c.internal_error("%s", aggrType.typeKind); 449 } 450 default: 451 c.internal_error(loc, "Cannot load from %s", source.kind); 452 } 453 } 454 455 /// writes value to a pointer or variable 456 void store(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock, IrIndex value) 457 { 458 CompilationContext* c = gen.context; 459 ExprValue destination = this; 460 //writefln("store %s %s", destination, value); 461 switch (destination.kind) 462 { 463 case ExprValueKind.ptr_to_ptr_to_data: 464 destination.irValue = destination.load(gen, loc, currentBlock); 465 goto case; 466 467 case ExprValueKind.ptr_to_data: 468 switch (destination.irValue.kind) with(IrValueKind) 469 { 470 case variable: 471 IrIndex ptr = gen.builder.readVariable(currentBlock, destination.irValue); 472 gen.builder.emitInstr!(IrOpcode.store)(currentBlock, ExtraInstrArgs(), ptr, value); 473 return; 474 case stackSlot, global, virtualRegister: 475 ExtraInstrArgs extra; 476 // destination must be a pointer 477 gen.builder.emitInstr!(IrOpcode.store)(currentBlock, extra, destination.irValue, value); 478 return; 479 480 default: break; 481 } 482 break; 483 484 case ExprValueKind.struct_sub_index: 485 IrIndex aggr = gen.builder.readVariable(currentBlock, destination.irValue); 486 IrIndex aggrType = gen.ir.getValueType(c, aggr); 487 //writefln("Store %s", IrIndexDump(aggrType, c, gen.ir)); 488 switch (aggrType.typeKind) { 489 case IrTypeKind.pointer: 490 IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32)); 491 IrIndex ptr = buildGEP(gen, loc, currentBlock, aggr, ZERO, destination.indices[]); 492 gen.builder.emitInstr!(IrOpcode.store)(currentBlock, ExtraInstrArgs(), ptr, value); 493 break; 494 case IrTypeKind.struct_t: 495 case IrTypeKind.array: 496 IrIndex[] args = gen.gepBuf[0..destination.indices.length+2]; 497 args[0] = aggr; 498 args[1] = value; 499 args[2..$] = destination.indices[]; 500 ExtraInstrArgs extra = { type : aggrType }; 501 IrIndex res = gen.builder.emitInstr!(IrOpcode.insert_element)(currentBlock, extra, args).result; 502 gen.builder.writeVariable(currentBlock, destination.irValue, res); 503 break; 504 default: c.internal_error("%s", aggrType.typeKind); 505 } 506 return; 507 508 default: 509 switch (destination.irValue.kind) with(IrValueKind) 510 { 511 case stackSlot, global, virtualRegister: 512 ExtraInstrArgs extra; 513 // destination must be a pointer 514 gen.builder.emitInstr!(IrOpcode.store)(currentBlock, extra, destination.irValue, value); 515 return; 516 case variable: 517 gen.builder.writeVariable(currentBlock, destination.irValue, value); 518 return; 519 default: 520 break; 521 } 522 } 523 c.internal_error(loc, "Cannot store into %s", destination.irValue.kind); 524 } 525 526 /// Returns reference to aggregate member 527 /// Index must be a constant when accessing struct members 528 ExprValue member(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock, IrIndex index) 529 { 530 CompilationContext* c = gen.context; 531 ExprValue aggr = this; 532 //writefln("getAggregateMember %s %s", aggr, index); 533 534 if (aggr.irValue.isVariable) { 535 switch (aggr.kind) with(ExprValueKind) 536 { 537 case value: 538 Array!IrIndex resIndices; 539 resIndices.put(c.arrayArena, index); 540 return ExprValue(aggr.irValue, ExprValueKind.struct_sub_index, IsLvalue.yes, resIndices); 541 case struct_sub_index: 542 aggr.indices.put(c.arrayArena, index); 543 return aggr; 544 default: 545 aggr.irValue = gen.builder.readVariable(currentBlock, aggr.irValue); 546 //writefln(" -1 %s", IrIndexDump(aggr.irValue, &gen.builder)); 547 break; 548 } 549 } 550 551 switch (aggr.kind) with(ExprValueKind) 552 { 553 case ptr_to_ptr_to_data: 554 aggr.irValue = aggr.load(gen, loc, currentBlock); 555 break; 556 default: break; 557 } 558 559 IrIndex aggrType = gen.ir.getValueType(c, aggr.irValue); 560 //writefln(" -2 %s", IrIndexDump(aggrType, &gen.builder)); 561 562 switch (aggrType.typeKind) { 563 case IrTypeKind.pointer: 564 IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32)); 565 return ExprValue(buildGEP(gen, loc, currentBlock, aggr.irValue, ZERO, index), ExprValueKind.ptr_to_data, IsLvalue.yes); 566 case IrTypeKind.struct_t: { 567 IrIndex aggrVal = aggr.irValue; 568 569 switch (aggrVal.kind) with(IrValueKind) { 570 case constantAggregate, constantZero: 571 aggrVal = c.constants.getAggregateMember(aggrVal, index, c); 572 assert(aggrVal.isDefined); 573 return ExprValue(aggrVal); 574 case virtualRegister: 575 IrIndex memberType = c.types.getAggregateMember(aggrType, c, index).type; 576 ExtraInstrArgs extra = { type : memberType }; 577 IrIndex[] args = gen.gepBuf[0..2]; 578 args[0] = aggrVal; 579 args[1] = index; 580 aggrVal = gen.builder.emitInstr!(IrOpcode.get_element)(currentBlock, extra, args).result; 581 return ExprValue(aggrVal); 582 default: 583 c.internal_error("Cannot read struct member from %s", aggrVal.kind); 584 } 585 } 586 default: c.internal_error("%s", aggrType.typeKind); 587 } 588 } 589 } 590 591 IrIndex buildGEPEx(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock, ExprValue aggrPtrExpr, IrIndex ptrIndex, IrIndex[] indices...) 592 { 593 CompilationContext* c = gen.context; 594 IrIndex aggrPtr = aggrPtrExpr.irValue; 595 if (aggrPtr.isVariable) { 596 aggrPtr = gen.builder.readVariable(currentBlock, aggrPtr); 597 } 598 switch (aggrPtrExpr.kind) with(ExprValueKind) 599 { 600 case ptr_to_data: break; 601 case struct_sub_index: 602 IrIndex aggrType = gen.ir.getValueType(c, aggrPtr); 603 switch (aggrType.typeKind) { 604 case IrTypeKind.pointer: 605 IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32)); 606 aggrPtr = buildGEP(gen, loc, currentBlock, aggrPtr, ZERO, aggrPtrExpr.indices[]); 607 break; 608 609 default: c.internal_error("aggrType.typeKind == %s", aggrType.typeKind); 610 } 611 break; 612 613 default: c.internal_error("aggrPtrExpr.kind == %s", aggrPtrExpr.kind); 614 } 615 return buildGEP(gen, loc, currentBlock, aggrPtr, ptrIndex, indices); 616 } 617 618 IrIndex buildGEP(ref IrGenState gen, TokenIndex loc, IrIndex currentBlock, IrIndex aggrPtr, IrIndex ptrIndex, IrIndex[] indices...) 619 { 620 CompilationContext* c = gen.context; 621 c.assertf(indices.length < MAX_GEP_INDICES, 622 "too much indices for GEP instruction (%s) > %s", 623 indices.length, MAX_GEP_INDICES); 624 625 if (aggrPtr.isVariable) { 626 aggrPtr = gen.builder.readVariable(currentBlock, aggrPtr); 627 } 628 629 IrIndex aggrPtrType = gen.ir.getValueType(c, aggrPtr); 630 IrIndex aggrType = c.types.getPointerBaseType(aggrPtrType); 631 632 foreach (i, IrIndex memberIndex; indices) 633 { 634 gen.gepBuf[i+2] = memberIndex; 635 final switch(aggrType.typeKind) 636 { 637 case IrTypeKind.basic: 638 c.internal_error(loc, "Cannot index basic type %s", aggrType.typeKind); 639 640 case IrTypeKind.pointer: 641 c.internal_error(loc, "Cannot index pointer with GEP instruction, use load first"); 642 643 case IrTypeKind.array: 644 aggrType = c.types.getArrayElementType(aggrType); 645 break; 646 647 case IrTypeKind.struct_t: 648 c.assertf(memberIndex.isSimpleConstant, loc, 649 "Structs can only be indexed with constants, not with %s", memberIndex); 650 aggrType = c.types.getAggregateMember(aggrType, c, memberIndex).type; 651 break; 652 653 case IrTypeKind.func_t: 654 c.internal_error(loc, "Cannot index function type"); 655 } 656 } 657 658 if (indices.length == 0 && ptrIndex.isConstantZero) 659 return aggrPtr; // skip no op GEP 660 661 ExtraInstrArgs extra = { type : c.types.appendPtr(aggrType) }; 662 IrIndex[] args = gen.gepBuf[0..indices.length+2]; 663 args[0] = aggrPtr; 664 args[1] = ptrIndex; 665 IrIndex result = gen.builder.emitInstr!(IrOpcode.get_element_ptr)(currentBlock, extra, args).result; 666 return result; 667 }