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