mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-28 06:00:46 +03:00

* WIP: Objective-C support * Further work on implementation * ObjC dynamic cast * Add swift stub class attribute * Classes, protocols and ivars * Fix compilation issues * Fix objc ir codegen * Add objc linker option * Add swift stub classref get ir gen * Minor cleanup * Fix objc link flag being added on non-darwin platforms * Refactor objc gen * remove use of std::nullopt * Emit protocol tables * Remove unused variable * Formatting * Fix build in release mode. Thanks for nothing, c++. * Fix consistency * Fix dynamic casts * Fix tocall parentfd ref and arm msgsend call * Make instance variables work * Implicitly add isa pointer to objc classes. * Fix protocol referencing & allow pragma mangle * Fix protocol linkage * Fix direct call support * always generate var type for methods * Fix test 16096a * Fix extern ivar symbol gen, retain method decls * Remove arm32 and x86 support * Check method and ivar info before pushing to member list * Make ObjcMethod info untyped. * Make ivar and method gen more robust * Generate optional protocol symbols * Use bitcasting instead of creating multiple type defs * Fix invalid protocol list struct gen * More codegen robustness * emit protocol table as const * Make protocol table anon struct * Fix callable type, generate protocol_list_t properly. * Cast vthis to argtype * Handle protorefs and classrefs properly * seperate label ref and deref * Fix method lookup * Enable objective-c tests * Enable objc_call_static test * Scan both classes and protocols for method ref * Enable objective-c tests on arm as well. * supress objc linker warning in tests * Fix class and protocol gen structure * Fix objc_protocol_sections test * ObjcMethod only get callee for functions with bodies * Fix protocol class method gen * Make ObjcMethod anon again * Fix missing emit calls * Fix classref gen * Implement some of the requested changes * Enable compilable tests * Fix property selector gen, ugly hack for final funcs. * Fix segfault in referencing fd->type * Refactor implementation * Fix null references in class and method lookup * include unordered_map * Get functionality on-par with prev impl. * Fix super context calls * Move -L-w flag to d_do_test and use IN_LLVM in objc.d/h * add LDC version tag to -L-w flag * Update CHANGELOG.md
881 lines
27 KiB
C++
881 lines
27 KiB
C++
//===-- tollvm.cpp --------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "gen/tollvm.h"
|
||
|
||
#include "dmd/aggregate.h"
|
||
#include "dmd/declaration.h"
|
||
#include "dmd/dsymbol.h"
|
||
#include "dmd/expression.h"
|
||
#include "dmd/id.h"
|
||
#include "dmd/init.h"
|
||
#include "dmd/module.h"
|
||
#include "dmd/target.h"
|
||
#include "driver/cl_options.h"
|
||
#include "gen/abi/abi.h"
|
||
#include "gen/arrays.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/complex.h"
|
||
#include "gen/dvalue.h"
|
||
#include "gen/functions.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/linkage.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/pragma.h"
|
||
#include "gen/runtime.h"
|
||
#include "gen/structs.h"
|
||
#include "gen/typinf.h"
|
||
#include "gen/uda.h"
|
||
#include "ir/irtype.h"
|
||
#include "ir/irtypeclass.h"
|
||
#include "ir/irtypefunction.h"
|
||
#include "ir/irtypestruct.h"
|
||
|
||
using namespace dmd;
|
||
|
||
bool DtoIsInMemoryOnly(Type *type) {
|
||
Type *typ = type->toBasetype();
|
||
TY t = typ->ty;
|
||
return (t == TY::Tstruct || t == TY::Tsarray);
|
||
}
|
||
|
||
bool DtoIsReturnInArg(CallExp *ce) {
|
||
Type *t = ce->e1->type->toBasetype();
|
||
if (t->ty == TY::Tfunction && (!ce->f || !DtoIsIntrinsic(ce->f))) {
|
||
return gABI->returnInArg(static_cast<TypeFunction *>(t),
|
||
ce->f && ce->f->needThis());
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void DtoAddExtendAttr(Type *type, llvm::AttrBuilder &attrs) {
|
||
type = type->toBasetype();
|
||
if (type->isintegral() && type->ty != TY::Tvector && size(type) <= 2) {
|
||
attrs.addAttribute(type->isunsigned() ? LLAttribute::ZExt
|
||
: LLAttribute::SExt);
|
||
}
|
||
}
|
||
|
||
LLType *DtoType(Type *t) {
|
||
t = stripModifiers(t);
|
||
|
||
if (t->ctype) {
|
||
return t->ctype->getLLType();
|
||
}
|
||
|
||
IF_LOG Logger::println("Building type: %s", t->toChars());
|
||
LOG_SCOPE;
|
||
|
||
switch (t->ty) {
|
||
// basic types
|
||
case TY::Tvoid:
|
||
case TY::Tint8:
|
||
case TY::Tuns8:
|
||
case TY::Tint16:
|
||
case TY::Tuns16:
|
||
case TY::Tint32:
|
||
case TY::Tuns32:
|
||
case TY::Tint64:
|
||
case TY::Tuns64:
|
||
case TY::Tint128:
|
||
case TY::Tuns128:
|
||
case TY::Tfloat32:
|
||
case TY::Tfloat64:
|
||
case TY::Tfloat80:
|
||
case TY::Timaginary32:
|
||
case TY::Timaginary64:
|
||
case TY::Timaginary80:
|
||
case TY::Tcomplex32:
|
||
case TY::Tcomplex64:
|
||
case TY::Tcomplex80:
|
||
// case TY::Tbit:
|
||
case TY::Tbool:
|
||
case TY::Tchar:
|
||
case TY::Twchar:
|
||
case TY::Tdchar:
|
||
case TY::Tnoreturn: {
|
||
return IrTypeBasic::get(t)->getLLType();
|
||
}
|
||
|
||
// pointers
|
||
case TY::Tnull:
|
||
case TY::Tpointer: {
|
||
return IrTypePointer::get(t)->getLLType();
|
||
}
|
||
|
||
// arrays
|
||
case TY::Tarray: {
|
||
return IrTypeArray::get(t)->getLLType();
|
||
}
|
||
|
||
case TY::Tsarray: {
|
||
return IrTypeSArray::get(t)->getLLType();
|
||
}
|
||
|
||
// aggregates
|
||
case TY::Tstruct:
|
||
case TY::Tclass: {
|
||
const auto isStruct = t->ty == TY::Tstruct;
|
||
AggregateDeclaration *ad;
|
||
if (isStruct) {
|
||
ad = static_cast<TypeStruct *>(t)->sym;
|
||
} else {
|
||
ad = static_cast<TypeClass *>(t)->sym;
|
||
}
|
||
if (ad->type->ty == TY::Terror) {
|
||
static LLStructType *opaqueErrorType =
|
||
LLStructType::create(gIR->context(), Type::terror->toChars());
|
||
return opaqueErrorType;
|
||
}
|
||
Type *adType = stripModifiers(ad->type);
|
||
if (adType->ctype) {
|
||
/* This should not happen, but e.g. can for aggregates whose mangled name
|
||
* contains a lambda which got promoted from a delegate to a function.
|
||
* We certainly don't want to override adType->ctype, and not associate
|
||
* an IrType to multiple Types either (see e.g.
|
||
* IrTypeStruct::resetDComputeTypes()).
|
||
* This means there are some aggregate Types which don't have an
|
||
* associated ctype, so getIrType() should always be fed with its
|
||
* AggregateDeclaration::type.
|
||
*/
|
||
IF_LOG {
|
||
Logger::println("Aggregate with multiple Types detected: %s (%s)",
|
||
ad->toPrettyChars(), ad->locToChars());
|
||
LOG_SCOPE;
|
||
Logger::println("Existing deco: %s", adType->deco);
|
||
Logger::println("Mismatching deco: %s", t->deco);
|
||
}
|
||
return adType->ctype->getLLType();
|
||
}
|
||
return isStruct ? IrTypeStruct::get(ad->isStructDeclaration())->getLLType()
|
||
: IrTypeClass::get(ad->isClassDeclaration())->getLLType();
|
||
}
|
||
|
||
// functions
|
||
case TY::Tfunction: {
|
||
return IrTypeFunction::get(t)->getLLType();
|
||
}
|
||
|
||
// delegates
|
||
case TY::Tdelegate: {
|
||
return IrTypeDelegate::get(t)->getLLType();
|
||
}
|
||
|
||
// typedefs
|
||
// enum
|
||
|
||
// FIXME: maybe just call toBasetype first ?
|
||
case TY::Tenum: {
|
||
Type *bt = t->toBasetype();
|
||
assert(bt);
|
||
if (t == bt) {
|
||
// This is an enum forward reference that is only legal when referenced
|
||
// through an indirection (e.g. "enum E; void foo(E* p);"). For lack of a
|
||
// better choice, make the outer indirection a void pointer.
|
||
return getI8Type();
|
||
}
|
||
return DtoType(bt);
|
||
}
|
||
|
||
// associative arrays
|
||
case TY::Taarray:
|
||
return getOpaquePtrType();
|
||
|
||
case TY::Tvector:
|
||
return IrTypeVector::get(t)->getLLType();
|
||
|
||
default:
|
||
llvm_unreachable("Unknown class of D Type!");
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
LLType *DtoMemType(Type *t) { return i1ToI8(voidToI8(DtoType(t))); }
|
||
|
||
LLType *voidToI8(LLType *t) {
|
||
return t->isVoidTy() ? LLType::getInt8Ty(t->getContext()) : t;
|
||
}
|
||
|
||
LLType *i1ToI8(LLType *t) {
|
||
return t->isIntegerTy(1) ? LLType::getInt8Ty(t->getContext()) : t;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLValue *DtoDelegateEquals(EXP op, LLValue *lhs, LLValue *rhs) {
|
||
Logger::println("Doing delegate equality");
|
||
if (rhs == nullptr) {
|
||
rhs = LLConstant::getNullValue(lhs->getType());
|
||
}
|
||
|
||
LLValue *l1 = gIR->ir->CreateExtractValue(lhs, 0);
|
||
LLValue *l2 = gIR->ir->CreateExtractValue(lhs, 1);
|
||
|
||
LLValue *r1 = gIR->ir->CreateExtractValue(rhs, 0);
|
||
LLValue *r2 = gIR->ir->CreateExtractValue(rhs, 1);
|
||
|
||
return createIPairCmp(op, l1, l2, r1, r2);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
namespace {
|
||
LLGlobalValue::LinkageTypes DtoLinkageOnly(Dsymbol *sym) {
|
||
if (hasWeakUDA(sym))
|
||
return LLGlobalValue::WeakAnyLinkage;
|
||
|
||
// static in ImportC translates to internal linkage
|
||
if (auto decl = sym->isDeclaration())
|
||
if ((decl->storage_class & STCstatic) && decl->isCsymbol())
|
||
return LLGlobalValue::InternalLinkage;
|
||
|
||
/* Function (incl. delegate) literals are emitted into each referencing
|
||
* compilation unit, so use internal linkage for all lambdas and all global
|
||
* variables they define.
|
||
* This makes sure these symbols don't accidentally collide when linking
|
||
* object files compiled by different compiler invocations (lambda mangles
|
||
* aren't stable - see https://issues.dlang.org/show_bug.cgi?id=23722).
|
||
*/
|
||
auto potentialLambda = sym;
|
||
if (auto vd = sym->isVarDeclaration())
|
||
if (vd->isDataseg())
|
||
potentialLambda = vd->toParent2();
|
||
if (potentialLambda->isFuncLiteralDeclaration())
|
||
return LLGlobalValue::InternalLinkage;
|
||
|
||
if (sym->isInstantiated())
|
||
return templateLinkage;
|
||
|
||
return LLGlobalValue::ExternalLinkage;
|
||
}
|
||
}
|
||
|
||
LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) {
|
||
const auto linkage = DtoLinkageOnly(sym);
|
||
const bool inCOMDAT = needsCOMDAT() ||
|
||
// ELF needs some help for ODR linkages:
|
||
// https://github.com/ldc-developers/ldc/issues/3589
|
||
(linkage == templateLinkage &&
|
||
global.params.targetTriple->isOSBinFormatELF());
|
||
return {linkage, inCOMDAT};
|
||
}
|
||
|
||
bool needsCOMDAT() {
|
||
/* For MSVC targets (and probably MinGW too), linkonce[_odr] and weak[_odr]
|
||
* linkages don't work and need to be emulated via COMDATs to prevent multiple
|
||
* definition errors when linking.
|
||
* Simply emit all functions in COMDATs, not just templates, for aggressive
|
||
* linker stripping (/OPT:REF and /OPT:ICF with MS linker/LLD), analogous to
|
||
* using /Gy with the MS compiler.
|
||
* https://docs.microsoft.com/en-us/cpp/build/reference/opt-optimizations?view=vs-2019
|
||
*/
|
||
return global.params.targetTriple->isOSBinFormatCOFF();
|
||
}
|
||
|
||
void setLinkage(LinkageWithCOMDAT lwc, llvm::GlobalObject *obj) {
|
||
obj->setLinkage(lwc.first);
|
||
obj->setComdat(lwc.second ? gIR->module.getOrInsertComdat(obj->getName())
|
||
: nullptr);
|
||
}
|
||
|
||
void setLinkageAndVisibility(Dsymbol *sym, llvm::GlobalObject *obj) {
|
||
setLinkage(DtoLinkage(sym), obj);
|
||
setVisibility(sym, obj);
|
||
}
|
||
|
||
namespace {
|
||
bool hasExportedLinkage(llvm::GlobalObject *obj) {
|
||
const auto l = obj->getLinkage();
|
||
return l == LLGlobalValue::ExternalLinkage ||
|
||
l == LLGlobalValue::WeakODRLinkage ||
|
||
l == LLGlobalValue::WeakAnyLinkage;
|
||
}
|
||
}
|
||
|
||
void setVisibility(Dsymbol *sym, llvm::GlobalObject *obj) {
|
||
const auto &triple = *global.params.targetTriple;
|
||
|
||
const bool hasHiddenUDA = obj->hasHiddenVisibility();
|
||
|
||
if (triple.isOSWindows()) {
|
||
bool isExported = sym->isExport();
|
||
// Also export (non-linkonce_odr) symbols
|
||
// * with -fvisibility=public without @hidden, or
|
||
// * if declared with dllimport (so potentially imported from other object
|
||
// files / DLLs).
|
||
if (!isExported && ((global.params.dllexport && !hasHiddenUDA) ||
|
||
obj->hasDLLImportStorageClass())) {
|
||
isExported = hasExportedLinkage(obj);
|
||
}
|
||
// reset default visibility & DSO locality - on Windows, the DLL storage
|
||
// classes matter
|
||
if (hasHiddenUDA) {
|
||
obj->setVisibility(LLGlobalValue::DefaultVisibility);
|
||
obj->setDSOLocal(false);
|
||
}
|
||
obj->setDLLStorageClass(isExported ? LLGlobalValue::DLLExportStorageClass
|
||
: LLGlobalValue::DefaultStorageClass);
|
||
} else {
|
||
if (sym->isExport()) {
|
||
obj->setVisibility(LLGlobalValue::DefaultVisibility); // overrides @hidden
|
||
} else if (!obj->hasLocalLinkage() && !hasHiddenUDA) {
|
||
// Hide with -fvisibility=hidden, or linkonce_odr etc.
|
||
// Note that symbols with local linkage cannot be hidden (LLVM assertion).
|
||
// The Apple linker warns about hidden linkonce_odr symbols from object
|
||
// files compiled with -linkonce-templates being folded with *public*
|
||
// weak_odr symbols from non-linkonce-templates code (e.g., Phobos), so
|
||
// don't hide instantiated symbols for Mac.
|
||
if (opts::symbolVisibility == opts::SymbolVisibility::hidden ||
|
||
(!hasExportedLinkage(obj) &&
|
||
!(triple.isOSDarwin() && sym->isInstantiated()))) {
|
||
obj->setVisibility(LLGlobalValue::HiddenVisibility);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLIntegerType *DtoSize_t() {
|
||
// the type of size_t does not change once set
|
||
static LLIntegerType *t = nullptr;
|
||
if (t == nullptr) {
|
||
if (target.ptrsize == 8) {
|
||
t = LLType::getInt64Ty(gIR->context());
|
||
} else if (target.ptrsize == 4) {
|
||
t = LLType::getInt32Ty(gIR->context());
|
||
} else if (target.ptrsize == 2) {
|
||
t = LLType::getInt16Ty(gIR->context());
|
||
} else {
|
||
llvm_unreachable("Unsupported size_t width");
|
||
}
|
||
}
|
||
return t;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
namespace {
|
||
llvm::GetElementPtrInst *DtoGEP(LLType *pointeeTy, LLValue *ptr,
|
||
llvm::ArrayRef<LLValue *> indices,
|
||
const char *name, llvm::BasicBlock *bb) {
|
||
auto gep = llvm::GetElementPtrInst::Create(pointeeTy, ptr, indices, name,
|
||
bb ? bb : gIR->scopebb());
|
||
gep->setIsInBounds(true);
|
||
return gep;
|
||
}
|
||
}
|
||
|
||
LLValue *DtoGEP1(LLType *pointeeTy, LLValue *ptr, LLValue *i0, const char *name,
|
||
llvm::BasicBlock *bb) {
|
||
return DtoGEP(pointeeTy, ptr, i0, name, bb);
|
||
}
|
||
|
||
LLValue *DtoGEP(LLType *pointeeTy, LLValue *ptr, LLValue *i0, LLValue *i1,
|
||
const char *name, llvm::BasicBlock *bb) {
|
||
LLValue *indices[] = {i0, i1};
|
||
return DtoGEP(pointeeTy, ptr, indices, name, bb);
|
||
}
|
||
|
||
LLValue *DtoGEP1(LLType *pointeeTy, LLValue *ptr, unsigned i0, const char *name,
|
||
llvm::BasicBlock *bb) {
|
||
return DtoGEP(pointeeTy, ptr, DtoConstUint(i0), name, bb);
|
||
}
|
||
|
||
LLValue *DtoGEP(LLType *pointeeTy, LLValue *ptr, unsigned i0, unsigned i1,
|
||
const char *name, llvm::BasicBlock *bb) {
|
||
LLValue *indices[] = {DtoConstUint(i0), DtoConstUint(i1)};
|
||
return DtoGEP(pointeeTy, ptr, indices, name, bb);
|
||
}
|
||
|
||
LLConstant *DtoGEP(LLType *pointeeTy, LLConstant *ptr, unsigned i0,
|
||
unsigned i1) {
|
||
LLValue *indices[] = {DtoConstUint(i0), DtoConstUint(i1)};
|
||
return llvm::ConstantExpr::getGetElementPtr(pointeeTy, ptr, indices,
|
||
/* InBounds = */ true);
|
||
}
|
||
|
||
LLValue *DtoGEP1i64(LLType *pointeeTy, LLValue *ptr, uint64_t i0, const char *name,
|
||
llvm::BasicBlock *bb) {
|
||
return DtoGEP(pointeeTy, ptr, DtoConstUlong(i0), name, bb);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoMemSet(LLValue *dst, LLValue *val, LLValue *nbytes, unsigned align) {
|
||
gIR->ir->CreateMemSet(dst, val, nbytes, llvm::MaybeAlign(align),
|
||
false /*isVolatile*/);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoMemSetZero(LLType *type, LLValue *dst, LLValue *nbytes, unsigned align) {
|
||
DtoMemSet(dst, DtoConstUbyte(0), nbytes, align);
|
||
}
|
||
|
||
void DtoMemSetZero(LLType *type, LLValue *dst, unsigned align) {
|
||
uint64_t n = getTypeStoreSize(type);
|
||
DtoMemSetZero(type, dst, DtoConstSize_t(n), align);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoMemCpy(LLValue *dst, LLValue *src, LLValue *nbytes, unsigned align) {
|
||
auto A = llvm::MaybeAlign(align);
|
||
gIR->ir->CreateMemCpy(dst, A, src, A, nbytes, false /*isVolatile*/);
|
||
}
|
||
|
||
void DtoMemCpy(LLType *type, LLValue *dst, LLValue *src, bool withPadding, unsigned align) {
|
||
uint64_t n =
|
||
withPadding ? getTypeAllocSize(type) : getTypeStoreSize(type);
|
||
DtoMemCpy(dst, src, DtoConstSize_t(n), align);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLValue *DtoMemCmp(LLValue *lhs, LLValue *rhs, LLValue *nbytes) {
|
||
// int memcmp ( const void * ptr1, const void * ptr2, size_t num );
|
||
|
||
LLFunction *fn = gIR->module.getFunction("memcmp");
|
||
if (!fn) {
|
||
LLType *ptrTy = getOpaquePtrType();
|
||
LLType *Tys[] = {ptrTy, ptrTy, DtoSize_t()};
|
||
LLFunctionType *fty =
|
||
LLFunctionType::get(LLType::getInt32Ty(gIR->context()), Tys, false);
|
||
fn = LLFunction::Create(fty, LLGlobalValue::ExternalLinkage, "memcmp",
|
||
&gIR->module);
|
||
}
|
||
|
||
return gIR->ir->CreateCall(fn, {lhs, rhs, nbytes});
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
llvm::ConstantInt *DtoConstSize_t(uint64_t i) {
|
||
return LLConstantInt::get(DtoSize_t(), i, false);
|
||
}
|
||
llvm::ConstantInt *DtoConstUlong(uint64_t i) {
|
||
return LLConstantInt::get(LLType::getInt64Ty(gIR->context()), i, false);
|
||
}
|
||
llvm::ConstantInt *DtoConstLong(int64_t i) {
|
||
return LLConstantInt::get(LLType::getInt64Ty(gIR->context()), i, true);
|
||
}
|
||
llvm::ConstantInt *DtoConstUint(unsigned i) {
|
||
return LLConstantInt::get(LLType::getInt32Ty(gIR->context()), i, false);
|
||
}
|
||
llvm::ConstantInt *DtoConstInt(int i) {
|
||
return LLConstantInt::get(LLType::getInt32Ty(gIR->context()), i, true);
|
||
}
|
||
llvm::ConstantInt *DtoConstUshort(uint16_t i) {
|
||
return LLConstantInt::get(LLType::getInt16Ty(gIR->context()), i, false);
|
||
}
|
||
LLConstant *DtoConstBool(bool b) {
|
||
return LLConstantInt::get(LLType::getInt1Ty(gIR->context()), b, false);
|
||
}
|
||
llvm::ConstantInt *DtoConstUbyte(unsigned char i) {
|
||
return LLConstantInt::get(LLType::getInt8Ty(gIR->context()), i, false);
|
||
}
|
||
|
||
LLConstant *DtoConstFP(Type *t, const real_t value) {
|
||
LLType *llty = DtoType(t);
|
||
assert(llty->isFloatingPointTy());
|
||
|
||
// 1) represent host real_t as llvm::APFloat
|
||
const auto &targetSemantics = llty->getFltSemantics();
|
||
APFloat v(targetSemantics, APFloat::uninitialized);
|
||
CTFloat::toAPFloat(value, v);
|
||
|
||
// 2) convert to target format
|
||
if (&v.getSemantics() != &targetSemantics) {
|
||
bool ignored;
|
||
v.convert(targetSemantics, APFloat::rmNearestTiesToEven, &ignored);
|
||
}
|
||
|
||
return LLConstantFP::get(gIR->context(), v);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLConstant *DtoConstCString(const char *str) {
|
||
llvm::StringRef s(str ? str : "");
|
||
LLGlobalVariable *gvar = gIR->getCachedStringLiteral(s);
|
||
return gvar;
|
||
}
|
||
|
||
LLConstant *DtoConstString(const char *str) {
|
||
LLConstant *cString = DtoConstCString(str);
|
||
LLConstant *length = DtoConstSize_t(str ? strlen(str) : 0);
|
||
return DtoConstSlice(length, cString);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
namespace {
|
||
llvm::LoadInst *DtoLoadImpl(LLType *type, LLValue *src, const char *name) {
|
||
return gIR->ir->CreateLoad(type, src, name);
|
||
}
|
||
}
|
||
|
||
LLValue *DtoLoad(LLType* type, LLValue *src, const char *name) {
|
||
return DtoLoadImpl(type, src, name);
|
||
}
|
||
|
||
LLValue *DtoLoad(DLValue *src, const char *name) {
|
||
return DtoLoadImpl(DtoType(src->type), DtoLVal(src), name);
|
||
}
|
||
|
||
// Like DtoLoad, but the pointer is guaranteed to be aligned appropriately for
|
||
// the type.
|
||
LLValue *DtoAlignedLoad(LLType *type, LLValue *src, const char *name) {
|
||
llvm::LoadInst *ld = DtoLoadImpl(type, src, name);
|
||
ld->setAlignment(gDataLayout->getABITypeAlign(ld->getType()));
|
||
return ld;
|
||
}
|
||
|
||
LLValue *DtoVolatileLoad(LLType *type, LLValue *src, const char *name) {
|
||
llvm::LoadInst *ld = DtoLoadImpl(type, src, name);
|
||
ld->setVolatile(true);
|
||
return ld;
|
||
}
|
||
|
||
void DtoStore(LLValue *src, LLValue *dst) {
|
||
assert(!src->getType()->isIntegerTy(1) &&
|
||
"Should store bools as i8 instead of i1.");
|
||
gIR->ir->CreateStore(src, dst);
|
||
}
|
||
|
||
void DtoVolatileStore(LLValue *src, LLValue *dst) {
|
||
assert(!src->getType()->isIntegerTy(1) &&
|
||
"Should store bools as i8 instead of i1.");
|
||
gIR->ir->CreateStore(src, dst)->setVolatile(true);
|
||
}
|
||
|
||
void DtoStoreZextI8(LLValue *src, LLValue *dst) {
|
||
if (src->getType()->isIntegerTy(1)) {
|
||
llvm::Type *i8 = llvm::Type::getInt8Ty(gIR->context());
|
||
src = gIR->ir->CreateZExt(src, i8);
|
||
}
|
||
gIR->ir->CreateStore(src, dst);
|
||
}
|
||
|
||
// Like DtoStore, but the pointer is guaranteed to be aligned appropriately for
|
||
// the type.
|
||
void DtoAlignedStore(LLValue *src, LLValue *dst) {
|
||
assert(!src->getType()->isIntegerTy(1) &&
|
||
"Should store bools as i8 instead of i1.");
|
||
llvm::StoreInst *st = gIR->ir->CreateStore(src, dst);
|
||
st->setAlignment(gDataLayout->getABITypeAlign(src->getType()));
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLType *stripAddrSpaces(LLType *t)
|
||
{
|
||
// Fastpath for normal compilation.
|
||
if(gIR->dcomputetarget == nullptr)
|
||
return t;
|
||
|
||
llvm::PointerType *pt = isaPointer(t);
|
||
if (!pt)
|
||
return t;
|
||
|
||
return getOpaquePtrType();
|
||
}
|
||
|
||
LLValue *DtoBitCast(LLValue *v, LLType *t, const llvm::Twine &name) {
|
||
// Strip addrspace qualifications from v before comparing types by pointer
|
||
// equality. This avoids the case where the pointer in { T addrspace(n)* }
|
||
// is dereferenced and generates a GEP -> (invalid) bitcast -> load sequence.
|
||
// Bitcasting of pointers between addrspaces is invalid in LLVM IR. Even if
|
||
// it were valid, it wouldn't be the desired outcome as we would always load
|
||
// from addrspace(0), instead of the addrspace of the pointer.
|
||
if (stripAddrSpaces(v->getType()) == t) {
|
||
return v;
|
||
}
|
||
assert(!isaStruct(t));
|
||
return gIR->ir->CreateBitCast(v, t, name);
|
||
}
|
||
|
||
LLConstant *DtoBitCast(LLConstant *v, LLType *t) {
|
||
// Refer to the explanation in the other DtoBitCast overloaded function.
|
||
if (stripAddrSpaces(v->getType()) == t) {
|
||
return v;
|
||
}
|
||
return llvm::ConstantExpr::getBitCast(v, t);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLValue *DtoInsertValue(LLValue *aggr, LLValue *v, unsigned idx,
|
||
const char *name) {
|
||
return gIR->ir->CreateInsertValue(aggr, v, idx, name);
|
||
}
|
||
|
||
LLValue *DtoExtractValue(LLValue *aggr, unsigned idx, const char *name) {
|
||
return gIR->ir->CreateExtractValue(aggr, idx, name);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLValue *DtoInsertElement(LLValue *vec, LLValue *v, LLValue *idx,
|
||
const char *name) {
|
||
return gIR->ir->CreateInsertElement(vec, v, idx, name);
|
||
}
|
||
|
||
LLValue *DtoExtractElement(LLValue *vec, LLValue *idx, const char *name) {
|
||
return gIR->ir->CreateExtractElement(vec, idx, name);
|
||
}
|
||
|
||
LLValue *DtoInsertElement(LLValue *vec, LLValue *v, unsigned idx,
|
||
const char *name) {
|
||
return DtoInsertElement(vec, v, DtoConstUint(idx), name);
|
||
}
|
||
|
||
LLValue *DtoExtractElement(LLValue *vec, unsigned idx, const char *name) {
|
||
return DtoExtractElement(vec, DtoConstUint(idx), name);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLPointerType *isaPointer(LLValue *v) {
|
||
return llvm::dyn_cast<LLPointerType>(v->getType());
|
||
}
|
||
|
||
LLPointerType *isaPointer(LLType *t) {
|
||
return llvm::dyn_cast<LLPointerType>(t);
|
||
}
|
||
|
||
LLArrayType *isaArray(LLValue *v) {
|
||
return llvm::dyn_cast<LLArrayType>(v->getType());
|
||
}
|
||
|
||
LLArrayType *isaArray(LLType *t) { return llvm::dyn_cast<LLArrayType>(t); }
|
||
|
||
LLStructType *isaStruct(LLValue *v) {
|
||
return llvm::dyn_cast<LLStructType>(v->getType());
|
||
}
|
||
|
||
LLStructType *isaStruct(LLType *t) { return llvm::dyn_cast<LLStructType>(t); }
|
||
|
||
LLFunctionType *isaFunction(LLValue *v) {
|
||
return llvm::dyn_cast<LLFunctionType>(v->getType());
|
||
}
|
||
|
||
LLFunctionType *isaFunction(LLType *t) {
|
||
return llvm::dyn_cast<LLFunctionType>(t);
|
||
}
|
||
|
||
LLConstant *isaConstant(LLValue *v) {
|
||
return llvm::dyn_cast<llvm::Constant>(v);
|
||
}
|
||
|
||
llvm::ConstantInt *isaConstantInt(LLValue *v) {
|
||
return llvm::dyn_cast<llvm::ConstantInt>(v);
|
||
}
|
||
|
||
llvm::Argument *isaArgument(LLValue *v) {
|
||
return llvm::dyn_cast<llvm::Argument>(v);
|
||
}
|
||
|
||
llvm::GlobalVariable *isaGlobalVar(LLValue *v) {
|
||
return llvm::dyn_cast<llvm::GlobalVariable>(v);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLGlobalVariable *makeGlobal(LLStringRef name, LLType* type, LLStringRef section, bool extern_, bool externInit) {
|
||
if (!type)
|
||
type = getOpaquePtrType();
|
||
|
||
auto var = new LLGlobalVariable(
|
||
gIR->module,
|
||
type,
|
||
false,
|
||
extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage,
|
||
nullptr,
|
||
name,
|
||
nullptr,
|
||
LLGlobalValue::NotThreadLocal,
|
||
0u,
|
||
externInit
|
||
);
|
||
|
||
if (!section.empty())
|
||
var->setSection(section);
|
||
|
||
return var;
|
||
}
|
||
|
||
LLGlobalVariable *makeGlobalWithBytes(LLStringRef name, LLConstantList packedContents, LLStructType* type, bool extern_, bool externInit) {
|
||
if (packedContents.empty()) {
|
||
packedContents.push_back(getNullPtr());
|
||
}
|
||
|
||
// Handle initializer.
|
||
LLConstant *init;
|
||
if (type) {
|
||
init = LLConstantStruct::get(
|
||
type,
|
||
packedContents
|
||
);
|
||
} else {
|
||
init = LLConstantStruct::getAnon(
|
||
packedContents,
|
||
true
|
||
);
|
||
type = reinterpret_cast<LLStructType *>(init->getType());
|
||
}
|
||
|
||
auto var = new LLGlobalVariable(
|
||
gIR->module,
|
||
type,
|
||
false,
|
||
extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage,
|
||
init,
|
||
name,
|
||
nullptr,
|
||
LLGlobalValue::NotThreadLocal,
|
||
0u,
|
||
externInit
|
||
);
|
||
|
||
return var;
|
||
}
|
||
|
||
LLGlobalVariable *makeGlobalRef(LLGlobalVariable *to, LLStringRef name, LLStringRef section, bool extern_, bool externInit) {
|
||
auto var = makeGlobal(name, to->getType(), section, extern_, externInit);
|
||
var->setInitializer(to);
|
||
return var;
|
||
}
|
||
|
||
LLGlobalVariable *makeGlobalStr(LLStringRef text, LLStringRef name, LLStringRef section, bool extern_, bool externInit) {
|
||
auto init = llvm::ConstantDataArray::getString(gIR->context(), text);
|
||
auto var = new LLGlobalVariable(
|
||
gIR->module,
|
||
init->getType(),
|
||
false,
|
||
extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage,
|
||
init,
|
||
name,
|
||
nullptr,
|
||
LLGlobalValue::NotThreadLocal,
|
||
0u,
|
||
externInit
|
||
);
|
||
|
||
if (!section.empty())
|
||
var->setSection(section);
|
||
return var;
|
||
}
|
||
|
||
LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer) {
|
||
auto global = gIR->module.getGlobalVariable(name, true);
|
||
if (global)
|
||
return global;
|
||
|
||
return makeGlobal(name, type, section, true, extInitializer);
|
||
}
|
||
|
||
LLGlobalVariable *getOrCreateWeak(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer) {
|
||
auto global = gIR->module.getGlobalVariable(name, true);
|
||
if (global)
|
||
return global;
|
||
|
||
global = makeGlobal(name, type, section, false, extInitializer);
|
||
global->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage);
|
||
global->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility);
|
||
return global;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLType *getI8Type() { return LLType::getInt8Ty(gIR->context()); }
|
||
|
||
LLType *getI16Type() { return LLType::getInt16Ty(gIR->context()); }
|
||
|
||
LLType *getI32Type() { return LLType::getInt32Ty(gIR->context()); }
|
||
|
||
LLType *getI64Type() { return LLType::getInt64Ty(gIR->context()); }
|
||
|
||
LLType *getSizeTType() { return DtoSize_t(); }
|
||
|
||
LLPointerType *getOpaquePtrType(unsigned addressSpace) {
|
||
return LLPointerType::get(gIR->context(), addressSpace);
|
||
}
|
||
|
||
llvm::ConstantPointerNull *getNullPtr() {
|
||
return llvm::ConstantPointerNull::get(getOpaquePtrType());
|
||
}
|
||
|
||
LLConstant *getNullValue(LLType *t) { return LLConstant::getNullValue(t); }
|
||
|
||
LLConstant *wrapNull(LLConstant *v) { return v ? v : getNullPtr(); }
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
size_t getTypeBitSize(LLType *t) { return gDataLayout->getTypeSizeInBits(t); }
|
||
|
||
size_t getTypeStoreSize(LLType *t) { return gDataLayout->getTypeStoreSize(t); }
|
||
|
||
size_t getTypeAllocSize(LLType *t) { return gDataLayout->getTypeAllocSize(t); }
|
||
|
||
size_t getPointerSize() { return gDataLayout->getPointerSize(0); }
|
||
|
||
size_t getPointerSizeInBits() { return gDataLayout->getPointerSizeInBits(0); }
|
||
|
||
unsigned int getABITypeAlign(LLType *t) {
|
||
return gDataLayout->getABITypeAlign(t).value();
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLStructType *DtoModuleReferenceType() {
|
||
if (gIR->moduleRefType) {
|
||
return gIR->moduleRefType;
|
||
}
|
||
|
||
auto ptrType = getOpaquePtrType();
|
||
LLType *elems[] = {ptrType, ptrType};
|
||
auto st = LLStructType::get(gIR->context(), elems, "ModuleReference");
|
||
|
||
gIR->moduleRefType = st;
|
||
return st;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLValue *DtoAggrPair(LLType *type, LLValue *V1, LLValue *V2, const char *name) {
|
||
LLValue *res = llvm::UndefValue::get(type);
|
||
res = gIR->ir->CreateInsertValue(res, V1, 0);
|
||
return gIR->ir->CreateInsertValue(res, V2, 1, name);
|
||
}
|
||
|
||
LLValue *DtoAggrPair(LLValue *V1, LLValue *V2, const char *name) {
|
||
LLType *types[] = {V1->getType(), V2->getType()};
|
||
LLType *t = LLStructType::get(gIR->context(), types, false);
|
||
return DtoAggrPair(t, V1, V2, name);
|
||
}
|
||
|
||
LLValue *DtoSlicePaint(LLValue *aggr, LLType *as) {
|
||
if (aggr->getType() == as) {
|
||
return aggr;
|
||
}
|
||
|
||
LLValue *res = llvm::UndefValue::get(as);
|
||
|
||
LLValue *V = gIR->ir->CreateExtractValue(aggr, 0);
|
||
V = DtoBitCast(V, as->getContainedType(0));
|
||
res = gIR->ir->CreateInsertValue(res, V, 0);
|
||
|
||
V = gIR->ir->CreateExtractValue(aggr, 1);
|
||
V = DtoBitCast(V, as->getContainedType(1));
|
||
return gIR->ir->CreateInsertValue(res, V, 1);
|
||
}
|