ldc/gen/tollvm.cpp
Luna 82878ef32c
Improve Objective-C support (#4777)
* 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
2024-12-03 04:26:27 +01:00

881 lines
27 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- 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);
}