mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-30 15:10:59 +03:00

And for sret args too, except for MSVC targets. At least on Win64, the sret align attributes lead to ICEs for std.bitmanip and std.uni (release only), when computing the known alignment in the LLVM inliner pass: Assertion failed: I->getParent()->getParent() == Q.CxtI->getParent()->getParent() && "Got assumption for the wrong function!", file C:\LDC\llvm\lib\Analysis\ValueTracking.cpp, line 711
1118 lines
37 KiB
C++
1118 lines
37 KiB
C++
//===-- functions.cpp -----------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "gen/functions.h"
|
||
|
||
#include "aggregate.h"
|
||
#include "declaration.h"
|
||
#include "id.h"
|
||
#include "init.h"
|
||
#include "module.h"
|
||
#include "mtype.h"
|
||
#include "statement.h"
|
||
#include "template.h"
|
||
#include "gen/abi.h"
|
||
#include "gen/arrays.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/dvalue.h"
|
||
#include "gen/inlineir.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/linkage.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/nested.h"
|
||
#include "gen/optimizer.h"
|
||
#include "gen/pragma.h"
|
||
#include "gen/runtime.h"
|
||
#include "gen/tollvm.h"
|
||
#include "ir/irfunction.h"
|
||
#include "ir/irmodule.h"
|
||
#include "llvm/IR/Intrinsics.h"
|
||
#include "llvm/IR/CFG.h"
|
||
#include <iostream>
|
||
|
||
llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype,
|
||
Type *nesttype, bool isMain, bool isCtor,
|
||
bool isIntrinsic) {
|
||
IF_LOG Logger::println("DtoFunctionType(%s)", type->toChars());
|
||
LOG_SCOPE
|
||
|
||
// sanity check
|
||
assert(type->ty == Tfunction);
|
||
TypeFunction *f = static_cast<TypeFunction *>(type);
|
||
assert(f->next && "Encountered function type with invalid return type; "
|
||
"trying to codegen function ignored by the frontend?");
|
||
|
||
// Return cached type if available
|
||
if (irFty.funcType) {
|
||
return irFty.funcType;
|
||
}
|
||
|
||
TargetABI *abi = (isIntrinsic ? TargetABI::getIntrinsic() : gABI);
|
||
|
||
// Do not modify irFty yet; this function may be called recursively if any
|
||
// of the argument types refer to this type.
|
||
IrFuncTy newIrFty;
|
||
|
||
// The index of the next argument on the LLVM level.
|
||
unsigned nextLLArgIdx = 0;
|
||
|
||
if (isMain) {
|
||
// _Dmain always returns i32, no matter what the type in the D main() is.
|
||
newIrFty.ret = new IrFuncTyArg(Type::tint32, false);
|
||
} else {
|
||
Type *rt = f->next;
|
||
const bool byref = f->isref && rt->toBasetype()->ty != Tvoid;
|
||
AttrBuilder attrBuilder;
|
||
|
||
if (abi->returnInArg(f)) {
|
||
// sret return
|
||
newIrFty.arg_sret = new IrFuncTyArg(
|
||
rt, true,
|
||
AttrBuilder().add(LLAttribute::StructRet).add(LLAttribute::NoAlias));
|
||
const unsigned alignment = DtoAlignment(rt);
|
||
if (alignment &&
|
||
// FIXME: LLVM inliner issues for std.bitmanip and std.uni on Win64
|
||
!global.params.targetTriple.isOSMSVCRT()) {
|
||
newIrFty.arg_sret->attrs.addAlignment(alignment);
|
||
}
|
||
rt = Type::tvoid;
|
||
++nextLLArgIdx;
|
||
} else {
|
||
// sext/zext return
|
||
attrBuilder.add(DtoShouldExtend(byref ? rt->pointerTo() : rt));
|
||
}
|
||
newIrFty.ret = new IrFuncTyArg(rt, byref, attrBuilder);
|
||
}
|
||
++nextLLArgIdx;
|
||
|
||
if (thistype) {
|
||
// Add the this pointer for member functions
|
||
AttrBuilder attrBuilder;
|
||
if (isCtor) {
|
||
attrBuilder.add(LLAttribute::Returned);
|
||
}
|
||
newIrFty.arg_this = new IrFuncTyArg(
|
||
thistype, thistype->toBasetype()->ty == Tstruct, attrBuilder);
|
||
++nextLLArgIdx;
|
||
} else if (nesttype) {
|
||
// Add the context pointer for nested functions
|
||
newIrFty.arg_nest = new IrFuncTyArg(nesttype, false);
|
||
++nextLLArgIdx;
|
||
}
|
||
|
||
// vararg functions are special too
|
||
if (f->varargs) {
|
||
if (f->linkage == LINKd) {
|
||
// d style with hidden args
|
||
// 2 (array) is handled by the frontend
|
||
if (f->varargs == 1) {
|
||
// _arguments
|
||
newIrFty.arg_arguments =
|
||
new IrFuncTyArg(Type::dtypeinfo->type->arrayOf(), false);
|
||
++nextLLArgIdx;
|
||
}
|
||
}
|
||
|
||
newIrFty.c_vararg = true;
|
||
}
|
||
|
||
// if this _Dmain() doesn't have an argument, we force it to have one
|
||
const size_t numExplicitDArgs = Parameter::dim(f->parameters);
|
||
|
||
if (isMain && numExplicitDArgs == 0) {
|
||
Type *mainargs = Type::tchar->arrayOf()->arrayOf();
|
||
newIrFty.args.push_back(new IrFuncTyArg(mainargs, false));
|
||
++nextLLArgIdx;
|
||
}
|
||
|
||
for (size_t i = 0; i < numExplicitDArgs; ++i) {
|
||
Parameter *arg = Parameter::getNth(f->parameters, i);
|
||
|
||
// Whether the parameter is passed by LLVM value or as a pointer to the
|
||
// alloca/….
|
||
bool passPointer = arg->storageClass & (STCref | STCout);
|
||
|
||
Type *loweredDType = arg->type;
|
||
AttrBuilder attrBuilder;
|
||
if (arg->storageClass & STClazy) {
|
||
// Lazy arguments are lowered to delegates.
|
||
Logger::println("lazy param");
|
||
auto ltf = new TypeFunction(nullptr, arg->type, 0, LINKd);
|
||
auto ltd = new TypeDelegate(ltf);
|
||
loweredDType = ltd;
|
||
} else if (!passPointer) {
|
||
if (abi->passByVal(loweredDType)) {
|
||
// LLVM ByVal parameters are pointers to a copy in the function
|
||
// parameters stack. The caller needs to provide a pointer to the
|
||
// original argument.
|
||
attrBuilder.addByVal(DtoAlignment(loweredDType));
|
||
passPointer = true;
|
||
} else {
|
||
// Add sext/zext as needed.
|
||
attrBuilder.add(DtoShouldExtend(loweredDType));
|
||
}
|
||
}
|
||
newIrFty.args.push_back(
|
||
new IrFuncTyArg(loweredDType, passPointer, attrBuilder));
|
||
newIrFty.args.back()->parametersIdx = i;
|
||
++nextLLArgIdx;
|
||
}
|
||
|
||
// let the ABI rewrite the types as necessary
|
||
abi->rewriteFunctionType(f, newIrFty);
|
||
|
||
// Now we can modify irFty safely.
|
||
irFty = llvm_move(newIrFty);
|
||
|
||
// Finally build the actual LLVM function type.
|
||
llvm::SmallVector<llvm::Type *, 16> argtypes;
|
||
argtypes.reserve(nextLLArgIdx);
|
||
|
||
if (irFty.arg_sret) {
|
||
argtypes.push_back(irFty.arg_sret->ltype);
|
||
}
|
||
if (irFty.arg_this) {
|
||
argtypes.push_back(irFty.arg_this->ltype);
|
||
}
|
||
if (irFty.arg_nest) {
|
||
argtypes.push_back(irFty.arg_nest->ltype);
|
||
}
|
||
if (irFty.arg_arguments) {
|
||
argtypes.push_back(irFty.arg_arguments->ltype);
|
||
}
|
||
|
||
if (irFty.arg_sret && irFty.arg_this && abi->passThisBeforeSret(f)) {
|
||
std::swap(argtypes[0], argtypes[1]);
|
||
}
|
||
|
||
const size_t firstExplicitArg = argtypes.size();
|
||
const size_t numExplicitLLArgs = irFty.args.size();
|
||
for (size_t i = 0; i < numExplicitLLArgs; i++) {
|
||
argtypes.push_back(irFty.args[i]->ltype);
|
||
}
|
||
|
||
// reverse params?
|
||
if (irFty.reverseParams && numExplicitLLArgs > 1) {
|
||
std::reverse(argtypes.begin() + firstExplicitArg, argtypes.end());
|
||
}
|
||
|
||
irFty.funcType =
|
||
LLFunctionType::get(irFty.ret->ltype, argtypes, irFty.c_vararg);
|
||
|
||
IF_LOG Logger::cout() << "Final function type: " << *irFty.funcType << "\n";
|
||
|
||
return irFty.funcType;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
static llvm::FunctionType *DtoVaFunctionType(FuncDeclaration *fdecl) {
|
||
IrFuncTy &irFty = getIrFunc(fdecl, true)->irFty;
|
||
if (irFty.funcType) {
|
||
return irFty.funcType;
|
||
}
|
||
|
||
irFty.ret = new IrFuncTyArg(Type::tvoid, false);
|
||
|
||
irFty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false));
|
||
|
||
if (fdecl->llvmInternal == LLVMva_start) {
|
||
irFty.funcType = GET_INTRINSIC_DECL(vastart)->getFunctionType();
|
||
} else if (fdecl->llvmInternal == LLVMva_copy) {
|
||
irFty.funcType = GET_INTRINSIC_DECL(vacopy)->getFunctionType();
|
||
irFty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false));
|
||
} else if (fdecl->llvmInternal == LLVMva_end) {
|
||
irFty.funcType = GET_INTRINSIC_DECL(vaend)->getFunctionType();
|
||
}
|
||
assert(irFty.funcType);
|
||
|
||
return irFty.funcType;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
llvm::FunctionType *DtoFunctionType(FuncDeclaration *fdecl) {
|
||
// handle for C vararg intrinsics
|
||
if (DtoIsVaIntrinsic(fdecl)) {
|
||
return DtoVaFunctionType(fdecl);
|
||
}
|
||
|
||
Type *dthis = nullptr, *dnest = nullptr;
|
||
|
||
if (fdecl->ident == Id::ensure || fdecl->ident == Id::require) {
|
||
FuncDeclaration *p = fdecl->parent->isFuncDeclaration();
|
||
assert(p);
|
||
AggregateDeclaration *ad = p->isMember2();
|
||
assert(ad);
|
||
dnest = Type::tvoid->pointerTo();
|
||
} else if (fdecl->needThis()) {
|
||
if (AggregateDeclaration *ad = fdecl->isMember2()) {
|
||
IF_LOG Logger::println("isMember = this is: %s", ad->type->toChars());
|
||
dthis = ad->type;
|
||
LLType *thisty = DtoType(dthis);
|
||
// Logger::cout() << "this llvm type: " << *thisty << '\n';
|
||
if (ad->isStructDeclaration()) {
|
||
thisty = getPtrToType(thisty);
|
||
}
|
||
} else {
|
||
IF_LOG Logger::println("chars: %s type: %s kind: %s", fdecl->toChars(),
|
||
fdecl->type->toChars(), fdecl->kind());
|
||
llvm_unreachable("needThis, but invalid parent declaration.");
|
||
}
|
||
} else if (fdecl->isNested()) {
|
||
dnest = Type::tvoid->pointerTo();
|
||
}
|
||
|
||
LLFunctionType *functype = DtoFunctionType(
|
||
fdecl->type, getIrFunc(fdecl, true)->irFty, dthis, dnest, fdecl->isMain(),
|
||
fdecl->isCtorDeclaration(), DtoIsIntrinsic(fdecl));
|
||
|
||
return functype;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
static llvm::Function *DtoDeclareVaFunction(FuncDeclaration *fdecl) {
|
||
DtoVaFunctionType(fdecl);
|
||
llvm::Function *func = nullptr;
|
||
|
||
if (fdecl->llvmInternal == LLVMva_start) {
|
||
func = GET_INTRINSIC_DECL(vastart);
|
||
} else if (fdecl->llvmInternal == LLVMva_copy) {
|
||
func = GET_INTRINSIC_DECL(vacopy);
|
||
} else if (fdecl->llvmInternal == LLVMva_end) {
|
||
func = GET_INTRINSIC_DECL(vaend);
|
||
}
|
||
assert(func);
|
||
|
||
getIrFunc(fdecl)->func = func;
|
||
return func;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoResolveFunction(FuncDeclaration *fdecl) {
|
||
if ((!global.params.useUnitTests || !fdecl->type) &&
|
||
fdecl->isUnitTestDeclaration()) {
|
||
IF_LOG Logger::println("Ignoring unittest %s", fdecl->toPrettyChars());
|
||
return; // ignore declaration completely
|
||
}
|
||
|
||
if (fdecl->ir.isResolved()) {
|
||
return;
|
||
}
|
||
fdecl->ir.setResolved();
|
||
|
||
Type *type = fdecl->type;
|
||
// If errors occurred compiling it, such as bugzilla 6118
|
||
if (type && type->ty == Tfunction) {
|
||
Type *next = static_cast<TypeFunction *>(type)->next;
|
||
if (!next || next->ty == Terror) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
// printf("resolve function: %s\n", fdecl->toPrettyChars());
|
||
|
||
if (fdecl->parent) {
|
||
if (TemplateInstance *tinst = fdecl->parent->isTemplateInstance()) {
|
||
if (TemplateDeclaration *tempdecl =
|
||
tinst->tempdecl->isTemplateDeclaration()) {
|
||
if (tempdecl->llvmInternal == LLVMva_arg) {
|
||
Logger::println("magic va_arg found");
|
||
fdecl->llvmInternal = LLVMva_arg;
|
||
fdecl->ir.setDefined();
|
||
return; // this gets mapped to an instruction so a declaration makes
|
||
// no sence
|
||
}
|
||
if (tempdecl->llvmInternal == LLVMva_start) {
|
||
Logger::println("magic va_start found");
|
||
fdecl->llvmInternal = LLVMva_start;
|
||
} else if (tempdecl->llvmInternal == LLVMintrinsic) {
|
||
Logger::println("overloaded intrinsic found");
|
||
assert(fdecl->llvmInternal == LLVMintrinsic);
|
||
assert(fdecl->mangleOverride);
|
||
} else if (tempdecl->llvmInternal == LLVMinline_asm) {
|
||
Logger::println("magic inline asm found");
|
||
TypeFunction *tf = static_cast<TypeFunction *>(fdecl->type);
|
||
if (tf->varargs != 1 ||
|
||
(fdecl->parameters && fdecl->parameters->dim != 0)) {
|
||
tempdecl->error("invalid __asm declaration, must be a D style "
|
||
"variadic with no explicit parameters");
|
||
fatal();
|
||
}
|
||
fdecl->llvmInternal = LLVMinline_asm;
|
||
fdecl->ir.setDefined();
|
||
return; // this gets mapped to a special inline asm call, no point in
|
||
// going on.
|
||
} else if (tempdecl->llvmInternal == LLVMinline_ir) {
|
||
Logger::println("magic inline ir found");
|
||
fdecl->llvmInternal = LLVMinline_ir;
|
||
fdecl->linkage = LINKc;
|
||
Type *type = fdecl->type;
|
||
assert(type->ty == Tfunction);
|
||
static_cast<TypeFunction *>(type)->linkage = LINKc;
|
||
|
||
DtoFunctionType(fdecl);
|
||
DtoDeclareFunction(fdecl);
|
||
fdecl->ir.setDefined();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
DtoFunctionType(fdecl);
|
||
|
||
IF_LOG Logger::println("DtoResolveFunction(%s): %s", fdecl->toPrettyChars(),
|
||
fdecl->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
// queue declaration unless the function is abstract without body
|
||
if (!fdecl->isAbstract() || fdecl->fbody) {
|
||
DtoDeclareFunction(fdecl);
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void applyParamAttrsToLLFunc(TypeFunction *f, IrFuncTy &irFty,
|
||
llvm::Function *func) {
|
||
AttrSet newAttrs = AttrSet::extractFunctionAndReturnAttributes(func);
|
||
newAttrs.merge(irFty.getParamAttrs(gABI->passThisBeforeSret(f)));
|
||
func->setAttributes(newAttrs);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoDeclareFunction(FuncDeclaration *fdecl) {
|
||
DtoResolveFunction(fdecl);
|
||
|
||
if (fdecl->ir.isDeclared()) {
|
||
return;
|
||
}
|
||
fdecl->ir.setDeclared();
|
||
|
||
IF_LOG Logger::println("DtoDeclareFunction(%s): %s", fdecl->toPrettyChars(),
|
||
fdecl->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
if (fdecl->isUnitTestDeclaration() && !global.params.useUnitTests) {
|
||
Logger::println("unit tests not enabled");
|
||
return;
|
||
}
|
||
|
||
// printf("declare function: %s\n", fdecl->toPrettyChars());
|
||
|
||
// intrinsic sanity check
|
||
if (DtoIsIntrinsic(fdecl) && fdecl->fbody) {
|
||
error(fdecl->loc, "intrinsics cannot have function bodies");
|
||
fatal();
|
||
}
|
||
|
||
// get TypeFunction*
|
||
Type *t = fdecl->type->toBasetype();
|
||
TypeFunction *f = static_cast<TypeFunction *>(t);
|
||
|
||
// create IrFunction
|
||
IrFunction *irFunc = getIrFunc(fdecl, true);
|
||
|
||
LLFunction *vafunc = nullptr;
|
||
if (DtoIsVaIntrinsic(fdecl)) {
|
||
vafunc = DtoDeclareVaFunction(fdecl);
|
||
}
|
||
|
||
// calling convention
|
||
LINK link = f->linkage;
|
||
if (vafunc || DtoIsIntrinsic(fdecl)
|
||
// DMD treats _Dmain as having C calling convention and this has been
|
||
// hardcoded into druntime, even if the frontend type has D linkage.
|
||
// See Bugzilla issue 9028.
|
||
|| fdecl->isMain()) {
|
||
link = LINKc;
|
||
}
|
||
|
||
// mangled name
|
||
std::string mangledName(mangleExact(fdecl));
|
||
mangledName = gABI->mangleForLLVM(mangledName, link);
|
||
|
||
// construct function
|
||
LLFunctionType *functype = DtoFunctionType(fdecl);
|
||
LLFunction *func = vafunc ? vafunc : gIR->module.getFunction(mangledName);
|
||
if (!func) {
|
||
if (fdecl->llvmInternal == LLVMinline_ir) {
|
||
func = DtoInlineIRFunction(fdecl);
|
||
} else {
|
||
// All function declarations are "external" - any other linkage type
|
||
// is set when actually defining the function.
|
||
func = LLFunction::Create(functype, llvm::GlobalValue::ExternalLinkage,
|
||
mangledName, &gIR->module);
|
||
}
|
||
} else if (func->getFunctionType() != functype) {
|
||
error(fdecl->loc, "Function type does not match previously declared "
|
||
"function with the same mangled name: %s",
|
||
mangleExact(fdecl));
|
||
fatal();
|
||
}
|
||
|
||
func->setCallingConv(gABI->callingConv(func->getFunctionType(), link));
|
||
|
||
IF_LOG Logger::cout() << "func = " << *func << std::endl;
|
||
|
||
// add func to IRFunc
|
||
irFunc->func = func;
|
||
|
||
// parameter attributes
|
||
if (!DtoIsIntrinsic(fdecl)) {
|
||
applyParamAttrsToLLFunc(f, getIrFunc(fdecl)->irFty, func);
|
||
if (global.params.disableRedZone) {
|
||
func->addFnAttr(LLAttribute::NoRedZone);
|
||
}
|
||
}
|
||
|
||
// main
|
||
if (fdecl->isMain()) {
|
||
// Detect multiple main functions, which is disallowed. DMD checks this
|
||
// in the glue code, so we need to do it here as well.
|
||
if (gIR->mainFunc) {
|
||
error(fdecl->loc, "only one main function allowed");
|
||
}
|
||
gIR->mainFunc = func;
|
||
}
|
||
|
||
if (fdecl->neverInline) {
|
||
irFunc->setNeverInline();
|
||
}
|
||
|
||
if (fdecl->llvmInternal == LLVMglobal_crt_ctor ||
|
||
fdecl->llvmInternal == LLVMglobal_crt_dtor) {
|
||
AppendFunctionToLLVMGlobalCtorsDtors(
|
||
func, fdecl->priority, fdecl->llvmInternal == LLVMglobal_crt_ctor);
|
||
}
|
||
|
||
IrFuncTy &irFty = irFunc->irFty;
|
||
|
||
// name parameters
|
||
llvm::Function::arg_iterator iarg = func->arg_begin();
|
||
|
||
const bool passThisBeforeSret =
|
||
irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(f);
|
||
|
||
if (irFty.arg_sret && !passThisBeforeSret) {
|
||
iarg->setName(".sret_arg");
|
||
irFunc->retArg = &(*iarg);
|
||
++iarg;
|
||
}
|
||
|
||
if (irFty.arg_this) {
|
||
iarg->setName(".this_arg");
|
||
irFunc->thisArg = &(*iarg);
|
||
|
||
VarDeclaration *v = fdecl->vthis;
|
||
if (v) {
|
||
// We already build the this argument here if we will need it
|
||
// later for codegen'ing the function, just as normal
|
||
// parameters below, because it can be referred to in nested
|
||
// context types. Will be given storage in DtoDefineFunction.
|
||
assert(!isIrParameterCreated(v));
|
||
IrParameter *irParam = getIrParameter(v, true);
|
||
irParam->value = &(*iarg);
|
||
irParam->arg = irFty.arg_this;
|
||
irParam->isVthis = true;
|
||
}
|
||
|
||
++iarg;
|
||
} else if (irFty.arg_nest) {
|
||
iarg->setName(".nest_arg");
|
||
irFunc->nestArg = &(*iarg);
|
||
assert(irFunc->nestArg);
|
||
++iarg;
|
||
}
|
||
|
||
if (passThisBeforeSret) {
|
||
iarg->setName(".sret_arg");
|
||
irFunc->retArg = &(*iarg);
|
||
++iarg;
|
||
}
|
||
|
||
if (irFty.arg_arguments) {
|
||
iarg->setName("._arguments");
|
||
irFunc->_arguments = &(*iarg);
|
||
++iarg;
|
||
}
|
||
|
||
unsigned int k = 0;
|
||
for (; iarg != func->arg_end(); ++iarg) {
|
||
size_t llExplicitIdx = irFty.reverseParams ? irFty.args.size() - k - 1 : k;
|
||
++k;
|
||
IrFuncTyArg *arg = irFty.args[llExplicitIdx];
|
||
|
||
if (!fdecl->parameters || arg->parametersIdx >= fdecl->parameters->dim) {
|
||
iarg->setName("unnamed");
|
||
continue;
|
||
}
|
||
|
||
Dsymbol *const argsym = (*fdecl->parameters)[arg->parametersIdx];
|
||
VarDeclaration *argvd = argsym->isVarDeclaration();
|
||
assert(argvd);
|
||
|
||
iarg->setName(argvd->ident->toChars() + llvm::Twine("_arg"));
|
||
|
||
IrParameter *irParam = getIrParameter(argvd, true);
|
||
irParam->arg = arg;
|
||
irParam->value = &(*iarg);
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
static LinkageWithCOMDAT lowerFuncLinkage(FuncDeclaration *fdecl) {
|
||
// Intrinsics are always external.
|
||
if (DtoIsIntrinsic(fdecl)) {
|
||
return LinkageWithCOMDAT(llvm::GlobalValue::ExternalLinkage, false);
|
||
}
|
||
|
||
// Generated array op functions behave like templates in that they might be
|
||
// emitted into many different modules.
|
||
if (fdecl->isArrayOp && (willInline() || !isDruntimeArrayOp(fdecl))) {
|
||
return LinkageWithCOMDAT(templateLinkage, supportsCOMDAT());
|
||
}
|
||
|
||
// A body-less declaration always needs to be marked as external in LLVM
|
||
// (also e.g. naked template functions which would otherwise be weak_odr,
|
||
// but where the definition is in module-level inline asm).
|
||
if (!fdecl->fbody || fdecl->naked) {
|
||
return LinkageWithCOMDAT(llvm::GlobalValue::ExternalLinkage, false);
|
||
}
|
||
|
||
return DtoLinkage(fdecl);
|
||
}
|
||
|
||
void DtoDefineFunction(FuncDeclaration *fd) {
|
||
IF_LOG Logger::println("DtoDefineFunction(%s): %s", fd->toPrettyChars(),
|
||
fd->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
if (fd->ir.isDefined()) {
|
||
return;
|
||
}
|
||
|
||
if ((fd->type && fd->type->ty == Terror) ||
|
||
(fd->type && fd->type->ty == Tfunction &&
|
||
static_cast<TypeFunction *>(fd->type)->next == nullptr) ||
|
||
(fd->type && fd->type->ty == Tfunction &&
|
||
static_cast<TypeFunction *>(fd->type)->next->ty == Terror)) {
|
||
IF_LOG Logger::println(
|
||
"Ignoring; has error type, no return type or returns error type");
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
|
||
if (fd->semanticRun == PASSsemanticdone) {
|
||
/* What happened is this function failed semantic3() with errors,
|
||
* but the errors were gagged.
|
||
* Try to reproduce those errors, and then fail.
|
||
*/
|
||
error(fd->loc, "errors compiling function %s", fd->toPrettyChars());
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
|
||
DtoResolveFunction(fd);
|
||
|
||
if (fd->isUnitTestDeclaration() && !global.params.useUnitTests) {
|
||
IF_LOG Logger::println("No code generation for unit test declaration %s",
|
||
fd->toChars());
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
|
||
// Skip array ops implemented in druntime
|
||
if (fd->isArrayOp && !willInline() && isDruntimeArrayOp(fd)) {
|
||
IF_LOG Logger::println(
|
||
"No code generation for array op %s implemented in druntime",
|
||
fd->toChars());
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
|
||
// Check whether the frontend knows that the function is already defined
|
||
// in some other module (see DMD's FuncDeclaration::toObjFile).
|
||
for (FuncDeclaration *f = fd; f;) {
|
||
if (!f->isInstantiated() && f->inNonRoot()) {
|
||
IF_LOG Logger::println("Skipping '%s'.", fd->toPrettyChars());
|
||
// TODO: Emit as available_externally for inlining purposes instead
|
||
// (see #673).
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
if (f->isNested()) {
|
||
f = f->toParent2()->isFuncDeclaration();
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
DtoDeclareFunction(fd);
|
||
assert(fd->ir.isDeclared());
|
||
|
||
// DtoResolveFunction might also set the defined flag for functions we
|
||
// should not touch.
|
||
if (fd->ir.isDefined()) {
|
||
return;
|
||
}
|
||
fd->ir.setDefined();
|
||
|
||
// We cannot emit nested functions with parents that have not gone through
|
||
// semantic analysis. This can happen as DMD leaks some template instances
|
||
// from constraints into the module member list. DMD gets away with being
|
||
// sloppy as functions in template contraints obviously never need to access
|
||
// data from the template function itself, but it would still mess up our
|
||
// nested context creation code.
|
||
FuncDeclaration *parent = fd;
|
||
while ((parent = getParentFunc(parent, true))) {
|
||
if (parent->semanticRun != PASSsemantic3done || parent->semantic3Errors) {
|
||
IF_LOG Logger::println(
|
||
"Ignoring nested function with unanalyzed parent.");
|
||
return;
|
||
}
|
||
}
|
||
|
||
assert(fd->semanticRun == PASSsemantic3done);
|
||
assert(fd->ident != Id::empty);
|
||
|
||
if (fd->isUnitTestDeclaration()) {
|
||
getIrModule(gIR->dmodule)->unitTests.push_back(fd);
|
||
} else if (fd->isSharedStaticCtorDeclaration()) {
|
||
getIrModule(gIR->dmodule)->sharedCtors.push_back(fd);
|
||
} else if (StaticDtorDeclaration *dtorDecl =
|
||
fd->isSharedStaticDtorDeclaration()) {
|
||
getIrModule(gIR->dmodule)->sharedDtors.push_front(fd);
|
||
if (dtorDecl->vgate) {
|
||
getIrModule(gIR->dmodule)->sharedGates.push_front(dtorDecl->vgate);
|
||
}
|
||
} else if (fd->isStaticCtorDeclaration()) {
|
||
getIrModule(gIR->dmodule)->ctors.push_back(fd);
|
||
} else if (StaticDtorDeclaration *dtorDecl = fd->isStaticDtorDeclaration()) {
|
||
getIrModule(gIR->dmodule)->dtors.push_front(fd);
|
||
if (dtorDecl->vgate) {
|
||
getIrModule(gIR->dmodule)->gates.push_front(dtorDecl->vgate);
|
||
}
|
||
}
|
||
|
||
// if this function is naked, we take over right away! no standard processing!
|
||
if (fd->naked) {
|
||
DtoDefineNakedFunction(fd);
|
||
return;
|
||
}
|
||
|
||
IrFunction *irFunc = getIrFunc(fd);
|
||
IrFuncTy &irFty = irFunc->irFty;
|
||
|
||
// debug info
|
||
irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd);
|
||
|
||
Type *t = fd->type->toBasetype();
|
||
TypeFunction *f = static_cast<TypeFunction *>(t);
|
||
// assert(f->ctype);
|
||
|
||
llvm::Function *func = irFunc->func;
|
||
|
||
// is there a body?
|
||
if (fd->fbody == nullptr) {
|
||
return;
|
||
}
|
||
|
||
IF_LOG Logger::println("Doing function body for: %s", fd->toChars());
|
||
gIR->functions.push_back(irFunc);
|
||
|
||
LinkageWithCOMDAT lwc = lowerFuncLinkage(fd);
|
||
func->setLinkage(lwc.first);
|
||
if (lwc.second) {
|
||
SET_COMDAT(func, gIR->module);
|
||
}
|
||
|
||
// On x86_64, always set 'uwtable' for System V ABI compatibility.
|
||
// TODO: Find a better place for this.
|
||
// TODO: Is this required for Win64 as well?
|
||
if (global.params.targetTriple.getArch() == llvm::Triple::x86_64) {
|
||
func->addFnAttr(LLAttribute::UWTable);
|
||
}
|
||
if (opts::sanitize != opts::None) {
|
||
// Set the required sanitizer attribute.
|
||
if (opts::sanitize == opts::AddressSanitizer) {
|
||
func->addFnAttr(LLAttribute::SanitizeAddress);
|
||
}
|
||
|
||
if (opts::sanitize == opts::MemorySanitizer) {
|
||
func->addFnAttr(LLAttribute::SanitizeMemory);
|
||
}
|
||
|
||
if (opts::sanitize == opts::ThreadSanitizer) {
|
||
func->addFnAttr(LLAttribute::SanitizeThread);
|
||
}
|
||
}
|
||
|
||
llvm::BasicBlock *beginbb =
|
||
llvm::BasicBlock::Create(gIR->context(), "", func);
|
||
|
||
// assert(gIR->scopes.empty());
|
||
gIR->scopes.push_back(IRScope(beginbb));
|
||
|
||
// create alloca point
|
||
// this gets erased when the function is complete, so alignment etc does not
|
||
// matter at all
|
||
llvm::Instruction *allocaPoint = new llvm::AllocaInst(
|
||
LLType::getInt32Ty(gIR->context()), "alloca point", beginbb);
|
||
irFunc->allocapoint = allocaPoint;
|
||
|
||
// debug info - after all allocas, but before any llvm.dbg.declare etc
|
||
gIR->DBuilder.EmitFuncStart(fd);
|
||
|
||
// this hack makes sure the frame pointer elimination optimization is
|
||
// disabled.
|
||
// this this eliminates a bunch of inline asm related issues.
|
||
if (fd->hasReturnExp & 8) // has inline asm
|
||
{
|
||
// emit a call to llvm_eh_unwind_init
|
||
LLFunction *hack = GET_INTRINSIC_DECL(eh_unwind_init);
|
||
#if LDC_LLVM_VER >= 307
|
||
gIR->ir->CreateCall(hack, {});
|
||
#else
|
||
gIR->ir->CreateCall(hack, "");
|
||
#endif
|
||
}
|
||
|
||
// give the 'this' argument storage and debug info
|
||
if (irFty.arg_this) {
|
||
LLValue *thisvar = irFunc->thisArg;
|
||
assert(thisvar);
|
||
|
||
LLValue *thismem = thisvar;
|
||
if (!irFty.arg_this->byref) {
|
||
thismem = DtoAllocaDump(thisvar, 0, "this");
|
||
irFunc->thisArg = thismem;
|
||
}
|
||
|
||
assert(getIrParameter(fd->vthis)->value == thisvar);
|
||
getIrParameter(fd->vthis)->value = thismem;
|
||
|
||
gIR->DBuilder.EmitLocalVariable(thismem, fd->vthis, nullptr, true);
|
||
}
|
||
|
||
// give the 'nestArg' storage
|
||
if (irFty.arg_nest) {
|
||
irFunc->nestArg = DtoAllocaDump(irFunc->nestArg, 0, "nestedFrame");
|
||
}
|
||
|
||
// give arguments storage and debug info
|
||
if (fd->parameters) {
|
||
// Not all arguments are necessarily passed on the LLVM level
|
||
// (e.g. zero-member structs), so we need to keep track of the
|
||
// index in the IrFuncTy args array separately.
|
||
size_t llArgIdx = 0;
|
||
for (size_t i = 0; i < fd->parameters->dim; ++i) {
|
||
Dsymbol *const argsym = (*fd->parameters)[i];
|
||
VarDeclaration *const vd = argsym->isVarDeclaration();
|
||
assert(vd);
|
||
const bool refout = vd->storage_class & (STCref | STCout);
|
||
|
||
IrParameter *irparam = getIrParameter(vd);
|
||
Type *debugInfoType = vd->type;
|
||
if (!irparam) {
|
||
// This is a parameter that is not passed on the LLVM level.
|
||
// Create the param here and set it to a "dummy" alloca that
|
||
// we do not store to here.
|
||
irparam = getIrParameter(vd, true);
|
||
irparam->value = DtoAlloca(vd, vd->ident->toChars());
|
||
} else {
|
||
const bool lazy = vd->storage_class & STClazy;
|
||
const bool firstClassVal = !refout && (!irparam->arg->byref || lazy);
|
||
if (firstClassVal) {
|
||
// alloca a stack slot for this first class value arg
|
||
LLValue *mem = DtoAlloca(irparam->arg->type, vd->ident->toChars());
|
||
|
||
// let the abi transform the argument back first
|
||
irFty.getParam(vd->type, llArgIdx, irparam->value, mem);
|
||
|
||
// set the arg var value to the alloca
|
||
irparam->value = mem;
|
||
|
||
debugInfoType = irparam->arg->type;
|
||
}
|
||
++llArgIdx;
|
||
}
|
||
|
||
if (global.params.symdebug &&
|
||
!(isaArgument(irparam->value) &&
|
||
isaArgument(irparam->value)->hasByValAttr())) {
|
||
gIR->DBuilder.EmitLocalVariable(irparam->value, vd, debugInfoType);
|
||
}
|
||
}
|
||
}
|
||
|
||
{
|
||
ScopeStack scopeStack(gIR);
|
||
irFunc->scopes = &scopeStack;
|
||
|
||
DtoCreateNestedContext(fd);
|
||
|
||
if (fd->vresult && !fd->vresult->nestedrefs.dim) // FIXME: not sure here :/
|
||
{
|
||
DtoVarDeclaration(fd->vresult);
|
||
}
|
||
|
||
// D varargs: prepare _argptr and _arguments
|
||
if (f->linkage == LINKd && f->varargs == 1) {
|
||
// allocate _argptr (of type core.stdc.stdarg.va_list)
|
||
LLValue *argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem");
|
||
irFunc->_argptr = argptrmem;
|
||
|
||
// initialize _argptr with a call to the va_start intrinsic
|
||
LLValue *vaStartArg = gABI->prepareVaStart(argptrmem);
|
||
llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "",
|
||
gIR->scopebb());
|
||
|
||
// copy _arguments to a memory location
|
||
irFunc->_arguments =
|
||
DtoAllocaDump(irFunc->_arguments, 0, "_arguments_mem");
|
||
}
|
||
|
||
// output function body
|
||
Statement_toIR(fd->fbody, gIR);
|
||
|
||
irFunc->scopes = nullptr;
|
||
}
|
||
|
||
llvm::BasicBlock *bb = gIR->scopebb();
|
||
if (pred_begin(bb) == pred_end(bb) &&
|
||
bb != &bb->getParent()->getEntryBlock()) {
|
||
// This block is trivially unreachable, so just delete it.
|
||
// (This is a common case because it happens when 'return'
|
||
// is the last statement in a function)
|
||
bb->eraseFromParent();
|
||
} else if (!gIR->scopereturned()) {
|
||
// llvm requires all basic blocks to end with a TerminatorInst but DMD does
|
||
// not put a return statement in automatically, so we do it here.
|
||
|
||
// pass the previous block into this block
|
||
gIR->DBuilder.EmitStopPoint(fd->endloc);
|
||
if (func->getReturnType() == LLType::getVoidTy(gIR->context())) {
|
||
gIR->ir->CreateRetVoid();
|
||
} else if (!fd->isMain()) {
|
||
CompoundAsmStatement *asmb = fd->fbody->endsWithAsm();
|
||
if (asmb) {
|
||
assert(asmb->abiret);
|
||
gIR->ir->CreateRet(asmb->abiret);
|
||
} else {
|
||
gIR->ir->CreateRet(llvm::UndefValue::get(func->getReturnType()));
|
||
}
|
||
} else {
|
||
gIR->ir->CreateRet(LLConstant::getNullValue(func->getReturnType()));
|
||
}
|
||
}
|
||
gIR->DBuilder.EmitFuncEnd(fd);
|
||
|
||
// erase alloca point
|
||
if (allocaPoint->getParent()) {
|
||
allocaPoint->eraseFromParent();
|
||
}
|
||
allocaPoint = nullptr;
|
||
gIR->func()->allocapoint = nullptr;
|
||
|
||
gIR->scopes.pop_back();
|
||
|
||
gIR->functions.pop_back();
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DValue *DtoArgument(Parameter *fnarg, Expression *argexp) {
|
||
IF_LOG Logger::println("DtoArgument");
|
||
LOG_SCOPE;
|
||
|
||
// ref/out arg
|
||
if (fnarg && (fnarg->storageClass & (STCref | STCout))) {
|
||
Loc loc;
|
||
DValue *arg = toElem(argexp, true);
|
||
return new DImValue(argexp->type,
|
||
arg->isLVal() ? arg->getLVal() : makeLValue(loc, arg));
|
||
}
|
||
|
||
DValue *arg = toElem(argexp);
|
||
|
||
// lazy arg
|
||
if (fnarg && (fnarg->storageClass & STClazy)) {
|
||
assert(argexp->type->toBasetype()->ty == Tdelegate);
|
||
assert(!arg->isLVal());
|
||
return arg;
|
||
}
|
||
|
||
// byval arg, but expr has no storage yet
|
||
if (DtoIsInMemoryOnly(argexp->type) && (arg->isSlice() || arg->isNull())) {
|
||
LLValue *alloc = DtoAlloca(argexp->type, ".tmp_arg");
|
||
auto vv = new DVarValue(argexp->type, alloc);
|
||
DtoAssign(argexp->loc, vv, arg);
|
||
arg = vv;
|
||
}
|
||
|
||
return arg;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
int binary(const char *p, const char **tab, int high) {
|
||
int i = 0, j = high, k, l;
|
||
do {
|
||
k = (i + j) / 2;
|
||
l = strcmp(p, tab[k]);
|
||
if (!l) {
|
||
return k;
|
||
}
|
||
if (l < 0) {
|
||
j = k;
|
||
} else {
|
||
i = k + 1;
|
||
}
|
||
} while (i != j);
|
||
return -1;
|
||
}
|
||
|
||
int isDruntimeArrayOp(FuncDeclaration *fd) {
|
||
/* Some of the array op functions are written as library functions,
|
||
* presumably to optimize them with special CPU vector instructions.
|
||
* List those library functions here, in alpha order.
|
||
*/
|
||
static const char *libArrayopFuncs[] = {
|
||
"_arrayExpSliceAddass_a", "_arrayExpSliceAddass_d",
|
||
"_arrayExpSliceAddass_f", // T[]+=T
|
||
"_arrayExpSliceAddass_g", "_arrayExpSliceAddass_h",
|
||
"_arrayExpSliceAddass_i", "_arrayExpSliceAddass_k",
|
||
"_arrayExpSliceAddass_s", "_arrayExpSliceAddass_t",
|
||
"_arrayExpSliceAddass_u", "_arrayExpSliceAddass_w",
|
||
|
||
"_arrayExpSliceDivass_d", // T[]/=T
|
||
"_arrayExpSliceDivass_f", // T[]/=T
|
||
|
||
"_arrayExpSliceMinSliceAssign_a",
|
||
"_arrayExpSliceMinSliceAssign_d", // T[]=T-T[]
|
||
"_arrayExpSliceMinSliceAssign_f", // T[]=T-T[]
|
||
"_arrayExpSliceMinSliceAssign_g", "_arrayExpSliceMinSliceAssign_h",
|
||
"_arrayExpSliceMinSliceAssign_i", "_arrayExpSliceMinSliceAssign_k",
|
||
"_arrayExpSliceMinSliceAssign_s", "_arrayExpSliceMinSliceAssign_t",
|
||
"_arrayExpSliceMinSliceAssign_u", "_arrayExpSliceMinSliceAssign_w",
|
||
|
||
"_arrayExpSliceMinass_a",
|
||
"_arrayExpSliceMinass_d", // T[]-=T
|
||
"_arrayExpSliceMinass_f", // T[]-=T
|
||
"_arrayExpSliceMinass_g", "_arrayExpSliceMinass_h",
|
||
"_arrayExpSliceMinass_i", "_arrayExpSliceMinass_k",
|
||
"_arrayExpSliceMinass_s", "_arrayExpSliceMinass_t",
|
||
"_arrayExpSliceMinass_u", "_arrayExpSliceMinass_w",
|
||
|
||
"_arrayExpSliceMulass_d", // T[]*=T
|
||
"_arrayExpSliceMulass_f", // T[]*=T
|
||
"_arrayExpSliceMulass_i", "_arrayExpSliceMulass_k",
|
||
"_arrayExpSliceMulass_s", "_arrayExpSliceMulass_t",
|
||
"_arrayExpSliceMulass_u", "_arrayExpSliceMulass_w",
|
||
|
||
"_arraySliceExpAddSliceAssign_a",
|
||
"_arraySliceExpAddSliceAssign_d", // T[]=T[]+T
|
||
"_arraySliceExpAddSliceAssign_f", // T[]=T[]+T
|
||
"_arraySliceExpAddSliceAssign_g", "_arraySliceExpAddSliceAssign_h",
|
||
"_arraySliceExpAddSliceAssign_i", "_arraySliceExpAddSliceAssign_k",
|
||
"_arraySliceExpAddSliceAssign_s", "_arraySliceExpAddSliceAssign_t",
|
||
"_arraySliceExpAddSliceAssign_u", "_arraySliceExpAddSliceAssign_w",
|
||
|
||
"_arraySliceExpDivSliceAssign_d", // T[]=T[]/T
|
||
"_arraySliceExpDivSliceAssign_f", // T[]=T[]/T
|
||
|
||
"_arraySliceExpMinSliceAssign_a",
|
||
"_arraySliceExpMinSliceAssign_d", // T[]=T[]-T
|
||
"_arraySliceExpMinSliceAssign_f", // T[]=T[]-T
|
||
"_arraySliceExpMinSliceAssign_g", "_arraySliceExpMinSliceAssign_h",
|
||
"_arraySliceExpMinSliceAssign_i", "_arraySliceExpMinSliceAssign_k",
|
||
"_arraySliceExpMinSliceAssign_s", "_arraySliceExpMinSliceAssign_t",
|
||
"_arraySliceExpMinSliceAssign_u", "_arraySliceExpMinSliceAssign_w",
|
||
|
||
"_arraySliceExpMulSliceAddass_d", // T[] += T[]*T
|
||
"_arraySliceExpMulSliceAddass_f", "_arraySliceExpMulSliceAddass_r",
|
||
|
||
"_arraySliceExpMulSliceAssign_d", // T[]=T[]*T
|
||
"_arraySliceExpMulSliceAssign_f", // T[]=T[]*T
|
||
"_arraySliceExpMulSliceAssign_i", "_arraySliceExpMulSliceAssign_k",
|
||
"_arraySliceExpMulSliceAssign_s", "_arraySliceExpMulSliceAssign_t",
|
||
"_arraySliceExpMulSliceAssign_u", "_arraySliceExpMulSliceAssign_w",
|
||
|
||
"_arraySliceExpMulSliceMinass_d", // T[] -= T[]*T
|
||
"_arraySliceExpMulSliceMinass_f", "_arraySliceExpMulSliceMinass_r",
|
||
|
||
"_arraySliceSliceAddSliceAssign_a",
|
||
"_arraySliceSliceAddSliceAssign_d", // T[]=T[]+T[]
|
||
"_arraySliceSliceAddSliceAssign_f", // T[]=T[]+T[]
|
||
"_arraySliceSliceAddSliceAssign_g", "_arraySliceSliceAddSliceAssign_h",
|
||
"_arraySliceSliceAddSliceAssign_i", "_arraySliceSliceAddSliceAssign_k",
|
||
"_arraySliceSliceAddSliceAssign_r", // T[]=T[]+T[]
|
||
"_arraySliceSliceAddSliceAssign_s", "_arraySliceSliceAddSliceAssign_t",
|
||
"_arraySliceSliceAddSliceAssign_u", "_arraySliceSliceAddSliceAssign_w",
|
||
|
||
"_arraySliceSliceAddass_a",
|
||
"_arraySliceSliceAddass_d", // T[]+=T[]
|
||
"_arraySliceSliceAddass_f", // T[]+=T[]
|
||
"_arraySliceSliceAddass_g", "_arraySliceSliceAddass_h",
|
||
"_arraySliceSliceAddass_i", "_arraySliceSliceAddass_k",
|
||
"_arraySliceSliceAddass_s", "_arraySliceSliceAddass_t",
|
||
"_arraySliceSliceAddass_u", "_arraySliceSliceAddass_w",
|
||
|
||
"_arraySliceSliceMinSliceAssign_a",
|
||
"_arraySliceSliceMinSliceAssign_d", // T[]=T[]-T[]
|
||
"_arraySliceSliceMinSliceAssign_f", // T[]=T[]-T[]
|
||
"_arraySliceSliceMinSliceAssign_g", "_arraySliceSliceMinSliceAssign_h",
|
||
"_arraySliceSliceMinSliceAssign_i", "_arraySliceSliceMinSliceAssign_k",
|
||
"_arraySliceSliceMinSliceAssign_r", // T[]=T[]-T[]
|
||
"_arraySliceSliceMinSliceAssign_s", "_arraySliceSliceMinSliceAssign_t",
|
||
"_arraySliceSliceMinSliceAssign_u", "_arraySliceSliceMinSliceAssign_w",
|
||
|
||
"_arraySliceSliceMinass_a",
|
||
"_arraySliceSliceMinass_d", // T[]-=T[]
|
||
"_arraySliceSliceMinass_f", // T[]-=T[]
|
||
"_arraySliceSliceMinass_g", "_arraySliceSliceMinass_h",
|
||
"_arraySliceSliceMinass_i", "_arraySliceSliceMinass_k",
|
||
"_arraySliceSliceMinass_s", "_arraySliceSliceMinass_t",
|
||
"_arraySliceSliceMinass_u", "_arraySliceSliceMinass_w",
|
||
|
||
"_arraySliceSliceMulSliceAssign_d", // T[]=T[]*T[]
|
||
"_arraySliceSliceMulSliceAssign_f", // T[]=T[]*T[]
|
||
"_arraySliceSliceMulSliceAssign_i", "_arraySliceSliceMulSliceAssign_k",
|
||
"_arraySliceSliceMulSliceAssign_s", "_arraySliceSliceMulSliceAssign_t",
|
||
"_arraySliceSliceMulSliceAssign_u", "_arraySliceSliceMulSliceAssign_w",
|
||
|
||
"_arraySliceSliceMulass_d", // T[]*=T[]
|
||
"_arraySliceSliceMulass_f", // T[]*=T[]
|
||
"_arraySliceSliceMulass_i", "_arraySliceSliceMulass_k",
|
||
"_arraySliceSliceMulass_s", "_arraySliceSliceMulass_t",
|
||
"_arraySliceSliceMulass_u", "_arraySliceSliceMulass_w",
|
||
};
|
||
char *name = fd->ident->toChars();
|
||
int i =
|
||
binary(name, libArrayopFuncs, sizeof(libArrayopFuncs) / sizeof(char *));
|
||
if (i != -1) {
|
||
return 1;
|
||
}
|
||
|
||
#ifdef DEBUG // Make sure our array is alphabetized
|
||
for (i = 0; i < sizeof(libArrayopFuncs) / sizeof(char *); i++) {
|
||
if (strcmp(name, libArrayopFuncs[i]) == 0)
|
||
assert(0);
|
||
}
|
||
#endif
|
||
return 0;
|
||
}
|