Mercurial > projects > ldc
comparison gen/toir.cpp @ 1479:4f7d50c744ed
Rewrite `StructLiteralExp::toElem` to store individual fields instead of
generating a constant to fill the entire struct with a single `store`.
This is much more efficient at compile time (fixing #320) and vastly reduces
the size of the emitted code. Since LLVM no longer needs to keep the data for
all fields in "registers" until the store happens, it should also be more
efficient at run time in cases where the fields aren't assigned with constants.
There's also some code clean-up by removing duplicated logic.
author | Frits van Bommel <fvbommel wxs.nl> |
---|---|
date | Sat, 06 Jun 2009 20:16:13 +0200 |
parents | dd72f56ad211 |
children | b4fc62f047cd |
comparison
equal
deleted
inserted
replaced
1478:4dca8ed9d8b7 | 1479:4f7d50c744ed |
---|---|
2407 return DtoConstSlice(DtoConstSize_t(elements->dim), globalstorePtr); | 2407 return DtoConstSlice(DtoConstSize_t(elements->dim), globalstorePtr); |
2408 } | 2408 } |
2409 | 2409 |
2410 ////////////////////////////////////////////////////////////////////////////////////////// | 2410 ////////////////////////////////////////////////////////////////////////////////////////// |
2411 | 2411 |
2412 // building a struct literal is pretty much the same as building a default initializer. | |
2413 | |
2414 extern size_t add_zeros(std::vector<llvm::Value*>& values, size_t diff); | |
2415 extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init); | 2412 extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init); |
2413 | |
2414 static LLValue* write_zeroes(LLValue* mem, unsigned start, unsigned end) { | |
2415 mem = DtoBitCast(mem, getVoidPtrType()); | |
2416 LLValue* gep = DtoGEPi1(mem, start, ".padding"); | |
2417 DtoMemSetZero(gep, DtoConstSize_t(end - start)); | |
2418 return mem; | |
2419 } | |
2416 | 2420 |
2417 DValue* StructLiteralExp::toElem(IRState* p) | 2421 DValue* StructLiteralExp::toElem(IRState* p) |
2418 { | 2422 { |
2419 Logger::print("StructLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars()); | 2423 Logger::print("StructLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars()); |
2420 LOG_SCOPE; | 2424 LOG_SCOPE; |
2421 | 2425 |
2422 // make sure the struct is fully resolved | 2426 // make sure the struct is fully resolved |
2423 sd->codegen(Type::sir); | 2427 sd->codegen(Type::sir); |
2424 | 2428 |
2425 // final list of values to put in the struct | 2429 // alloca a stack slot |
2426 std::vector<LLValue*> initvalues; | 2430 LLValue* mem = DtoRawAlloca(DtoType(type), 0, ".structliteral"); |
2427 | |
2428 // offset tracker | |
2429 size_t offset = 0; | |
2430 | |
2431 // align(1) struct S { ... } | |
2432 bool packed = sd->type->alignsize() == 1; | |
2433 | 2431 |
2434 // ready elements data | 2432 // ready elements data |
2435 assert(elements && "struct literal has null elements"); | 2433 assert(elements && "struct literal has null elements"); |
2436 size_t nexprs = elements->dim;; | 2434 size_t nexprs = elements->dim; |
2437 Expression** exprs = (Expression**)elements->data; | 2435 Expression** exprs = (Expression**)elements->data; |
2436 | |
2437 LLValue* voidptr = mem; | |
2438 unsigned offset = 0; | |
2438 | 2439 |
2439 // go through fields | 2440 // go through fields |
2440 ArrayIter<VarDeclaration> it(sd->fields); | 2441 ArrayIter<VarDeclaration> it(sd->fields); |
2441 for (; !it.done(); it.next()) | 2442 for (; !it.done(); it.next()) |
2442 { | 2443 { |
2443 VarDeclaration* vd = it.get(); | 2444 VarDeclaration* vd = it.get(); |
2444 | 2445 |
2446 // don't re-initialize unions | |
2445 if (vd->offset < offset) | 2447 if (vd->offset < offset) |
2446 { | 2448 { |
2447 IF_LOG Logger::println("skipping field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); | 2449 IF_LOG Logger::println("skipping field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); |
2448 continue; | 2450 continue; |
2449 } | 2451 } |
2450 | 2452 // initialize any padding so struct comparisons work |
2451 IF_LOG Logger::println("using field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); | 2453 if (vd->offset != offset) |
2452 | 2454 voidptr = write_zeroes(voidptr, offset, vd->offset); |
2453 // get next aligned offset for this field | 2455 offset = vd->offset + vd->type->size(); |
2454 size_t alignedoffset = offset; | 2456 |
2455 if (!packed) | 2457 IF_LOG Logger::println("initializing field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); |
2456 { | 2458 |
2457 alignedoffset = realignOffset(alignedoffset, vd->type); | 2459 // get initializer |
2458 } | |
2459 | |
2460 // insert explicit padding? | |
2461 if (alignedoffset < vd->offset) | |
2462 { | |
2463 add_zeros(initvalues, vd->offset - alignedoffset); | |
2464 } | |
2465 | |
2466 // add initializer | |
2467 Expression* expr = (it.index < nexprs) ? exprs[it.index] : NULL; | 2460 Expression* expr = (it.index < nexprs) ? exprs[it.index] : NULL; |
2468 IF_LOG Logger::println("expr: %p", expr); | 2461 IF_LOG Logger::println("expr: %p", expr); |
2462 DValue* val; | |
2463 DConstValue cv(vd->type, NULL); // Only used in one branch; value is set beforehand | |
2469 if (expr) | 2464 if (expr) |
2470 { | 2465 { |
2471 IF_LOG Logger::println("expr %zu = %s", it.index, expr->toChars()); | 2466 IF_LOG Logger::println("expr %zu = %s", it.index, expr->toChars()); |
2472 LLValue* v = DtoExprValue(vd->type, expr); | 2467 val = expr->toElem(gIR); |
2473 initvalues.push_back(v); | |
2474 } | 2468 } |
2475 else | 2469 else |
2476 { | 2470 { |
2477 IF_LOG Logger::println("using default initializer"); | 2471 IF_LOG Logger::println("using default initializer"); |
2478 initvalues.push_back(get_default_initializer(vd, NULL)); | 2472 cv.c = get_default_initializer(vd, NULL); |
2479 } | 2473 val = &cv; |
2480 | 2474 } |
2481 // advance offset to right past this field | 2475 |
2482 offset = vd->offset + vd->type->size(); | 2476 // get a pointer to this field |
2483 } | 2477 DVarValue field(vd->type, vd, DtoIndexStruct(mem, sd, vd)); |
2484 | 2478 |
2485 // tail padding? | 2479 // store the initializer there |
2486 if (offset < sd->structsize) | 2480 DtoAssign(loc, &field, val); |
2487 { | 2481 } |
2488 add_zeros(initvalues, sd->structsize - offset); | 2482 // initialize trailing padding |
2489 } | 2483 if (sd->structsize != offset) |
2490 | 2484 write_zeroes(voidptr, offset, sd->structsize); |
2491 // build type | |
2492 std::vector<const LLType*> valuetypes; | |
2493 | |
2494 size_t n = initvalues.size(); | |
2495 valuetypes.reserve(n); | |
2496 | |
2497 for (size_t i = 0; i < n; i++) | |
2498 { | |
2499 valuetypes.push_back(initvalues[i]->getType()); | |
2500 } | |
2501 | |
2502 const LLType* st = llvm::StructType::get(valuetypes, packed); | |
2503 | |
2504 // alloca a stack slot | |
2505 LLValue* mem = DtoRawAlloca(st, 0, ".structliteral"); | |
2506 | |
2507 // fill in values | |
2508 for (size_t i = 0; i < n; i++) | |
2509 { | |
2510 LLValue* addr = DtoGEPi(mem, 0, i); | |
2511 p->ir->CreateStore(initvalues[i], addr); | |
2512 } | |
2513 | |
2514 // cast to default struct type | |
2515 mem = DtoBitCast(mem, DtoType(sd->type->pointerTo())); | |
2516 | 2485 |
2517 // return as a var | 2486 // return as a var |
2518 return new DVarValue(type, mem); | 2487 return new DVarValue(type, mem); |
2519 } | 2488 } |
2520 | 2489 |