From 54836a4ee0f056ea17cdc4dc181b0fd6b78c18fe Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sun, 25 Sep 2022 16:22:47 +0800 Subject: [PATCH] Pass slices as not a struct --- gen/functions.cpp | 40 ++++++++++++++++++++++++++++------ gen/tocall.cpp | 8 +++++-- gen/uda.cpp | 24 +++++++++++++++++--- gen/uda.h | 2 ++ ir/irfuncty.cpp | 9 ++++---- ir/irfuncty.h | 9 ++++++-- ir/irvar.h | 2 ++ tests/codegen/restrict_slice.d | 12 ++++++++++ 8 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 tests/codegen/restrict_slice.d diff --git a/gen/functions.cpp b/gen/functions.cpp index f54f5fbd1b0..6cf89781965 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -117,14 +117,14 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, sretAttrs.addAttribute(LLAttribute::NoAlias); if (unsigned alignment = DtoAlignment(rt)) sretAttrs.addAlignmentAttr(alignment); - newIrFty.arg_sret = new IrFuncTyArg(rt, true, std::move(sretAttrs)); + newIrFty.arg_sret = new IrFuncTyArg(rt, true, false, std::move(sretAttrs)); rt = Type::tvoid; ++nextLLArgIdx; } else { // sext/zext return DtoAddExtendAttr(byref ? rt->pointerTo() : rt, attrs); } - newIrFty.ret = new IrFuncTyArg(rt, byref, std::move(attrs)); + newIrFty.ret = new IrFuncTyArg(rt, byref, false, std::move(attrs)); } ++nextLLArgIdx; @@ -140,7 +140,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, attrs.addAttribute(LLAttribute::Returned); } newIrFty.arg_this = new IrFuncTyArg( - thistype, thistype->toBasetype()->ty == TY::Tstruct, std::move(attrs)); + thistype, thistype->toBasetype()->ty == TY::Tstruct, false, std::move(attrs)); ++nextLLArgIdx; } else if (nesttype) { // Add the context pointer for nested functions @@ -150,7 +150,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, llvm::AttrBuilder attrs; #endif attrs.addAttribute(LLAttribute::NonNull); - newIrFty.arg_nest = new IrFuncTyArg(nesttype, false, std::move(attrs)); + newIrFty.arg_nest = new IrFuncTyArg(nesttype, false, false, std::move(attrs)); ++nextLLArgIdx; } @@ -237,9 +237,22 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, } } - newIrFty.args.push_back(new IrFuncTyArg(loweredDType, passPointer, std::move(attrs))); - newIrFty.args.back()->parametersIdx = i; - ++nextLLArgIdx; + if (loweredDType->isTypeDArray() && !passPointer && hasRestrict(arg->userAttribDecl)) { +#if LDC_LLVM_VER >= 1400 + llvm::AttrBuilder length_attrs(getGlobalContext()); +#else + llvm::AttrBuilder length_attrs; +#endif + newIrFty.args.push_back(new IrFuncTyArg(Type::tsize_t, false, true, std::move(length_attrs))); + newIrFty.args.back()->parametersIdx = -1; + newIrFty.args.push_back(new IrFuncTyArg(loweredDType->nextOf()->pointerTo(), false, false, std::move(attrs))); + newIrFty.args.back()->parametersIdx = i; + nextLLArgIdx += 2; + } else { + newIrFty.args.push_back(new IrFuncTyArg(loweredDType, passPointer, false, std::move(attrs))); + newIrFty.args.back()->parametersIdx = i; + ++nextLLArgIdx; + } } // let the ABI rewrite the types as necessary @@ -800,6 +813,19 @@ void DtoDeclareFunction(FuncDeclaration *fdecl, const bool willDefine) { for (; iarg != func->arg_end(); ++iarg) { IrFuncTyArg *arg = irFty.args[k++]; + if (arg->isArrayLength) { + size_t idx = irFty.args[k]->parametersIdx; + auto *const vd = (*fdecl->parameters)[idx]; + iarg->setName(vd->ident->toChars() + llvm::Twine("_arg_len")); + IrParameter *irParam = getIrParameter(vd, true); + irParam->arg = arg; + irParam->value = &(*iarg); + ++iarg; + iarg->setName(vd->ident->toChars() + llvm::Twine("_arg_ptr")); + irParam->arg2 = irFty.args[k++];; + irParam->value2 = &(*iarg); + continue; + } if (!fdecl->parameters || arg->parametersIdx >= fdecl->parameters->length) { iarg->setName("unnamed"); continue; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 4613b63393e..a74c8dec58a 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -114,7 +114,7 @@ static void addExplicitArguments(std::vector &args, AttrSet &attrs, DtoAddExtendAttr(argType, initialAttrs); } - optionalIrArgs.push_back(new IrFuncTyArg(argType, passByVal, std::move(initialAttrs))); + optionalIrArgs.push_back(new IrFuncTyArg(argType, passByVal, false, std::move(initialAttrs))); optionalIrArgs.back()->parametersIdx = i; } @@ -188,7 +188,11 @@ static void addExplicitArguments(std::vector &args, AttrSet &attrs, Logger::cout() << "expects: " << *paramType << '\n'; } if (isaStruct(llVal)) { - llVal = DtoSlicePaint(llVal, paramType); + args[llArgIdx] = DtoExtractValue(llVal, 0); + attrs.addToParam(llArgIdx + 1, irArg->attrs); + args[llArgIdx + 1] = DtoExtractValue(llVal, 1); + ++i; + continue; } else { llVal = DtoBitCast(llVal, paramType); } diff --git a/gen/uda.cpp b/gen/uda.cpp index 791f1584741..fb1b23b9900 100644 --- a/gen/uda.cpp +++ b/gen/uda.cpp @@ -93,13 +93,15 @@ void checkStructElems(StructLiteralExp *sle, ArrayParam elemTypes) { /// Returns the StructLiteralExp magic attribute with identifier `id` from /// the ldc magic module with identifier `from` (attributes or dcompute) /// if it is applied to `sym`, otherwise returns nullptr. -StructLiteralExp *getMagicAttribute(Dsymbol *sym, const Identifier *id, +/// +StructLiteralExp *getMagicAttribute(UserAttributeDeclaration* uattrs, + const Identifier *id, const Identifier *from) { - if (!sym->userAttribDecl) + if (!uattrs) return nullptr; // Loop over all UDAs and early return the expression if a match was found. - Expressions *attrs = sym->userAttribDecl->getAttributes(); + Expressions *attrs = uattrs->getAttributes(); expandTuples(attrs); for (auto attr : *attrs) { if (auto sle = attr->isStructLiteralExp()) @@ -109,6 +111,13 @@ StructLiteralExp *getMagicAttribute(Dsymbol *sym, const Identifier *id, return nullptr; } +StructLiteralExp *getMagicAttribute(Dsymbol *sym, const Identifier *id, + const Identifier *from) { + if (!sym->userAttribDecl) + return nullptr; + + return getMagicAttribute(sym->userAttribDecl, id, from); +} /// Calls `action` for each magic attribute with identifier `id` from /// the ldc magic module with identifier `from` (attributes or dcompute) @@ -542,6 +551,15 @@ bool hasKernelAttr(Dsymbol *sym) { return true; } +bool hasRestrict(UserAttributeDeclaration *uattrs) { + auto sle = getMagicAttribute(uattrs, Id::udaLLVMAttr, Id::attributes); + if (!sle) + return false; + + checkStructElems(sle, {Type::tstring, Type::tstring}); + return getStringElem(sle, 0) == "noalias"; +} + /// Creates a mask (for &) of @ldc.attributes.noSanitize UDA applied to the /// function. /// If a bit is set in the mask, then the sanitizer is enabled. diff --git a/gen/uda.h b/gen/uda.h index 15b531bd52a..306f7cff75d 100644 --- a/gen/uda.h +++ b/gen/uda.h @@ -15,6 +15,7 @@ #pragma once class Dsymbol; +class UserAttributeDeclaration; class FuncDeclaration; class VarDeclaration; struct IrFunction; @@ -27,6 +28,7 @@ void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar); bool hasWeakUDA(Dsymbol *sym); bool hasKernelAttr(Dsymbol *sym); +bool hasRestrict(UserAttributeDeclaration *uattrs); /// Must match ldc.dcompute.Compilefor + 1 == DComputeCompileFor enum class DComputeCompileFor : int { diff --git a/ir/irfuncty.cpp b/ir/irfuncty.cpp index 595c5b04bb8..aa9711ba9ae 100644 --- a/ir/irfuncty.cpp +++ b/ir/irfuncty.cpp @@ -17,20 +17,21 @@ #include "gen/logger.h" #include "gen/tollvm.h" -IrFuncTyArg::IrFuncTyArg(Type *t, bool bref) +IrFuncTyArg::IrFuncTyArg(Type *t, bool bref, bool isArrayLength) : type(t), ltype(t != Type::tvoid && bref ? DtoType(t->pointerTo()) : DtoType(t)), #if LDC_LLVM_VER >= 1400 attrs(getGlobalContext()), #endif - byref(bref) { + byref(bref), + isArrayLength(isArrayLength) { mem.addRange(&type, sizeof(type)); } -IrFuncTyArg::IrFuncTyArg(Type *t, bool bref, llvm::AttrBuilder a) +IrFuncTyArg::IrFuncTyArg(Type *t, bool bref, bool isArrayLength, llvm::AttrBuilder a) : type(t), ltype(t != Type::tvoid && bref ? DtoType(t->pointerTo()) : DtoType(t)), - attrs(std::move(a)), byref(bref) { + attrs(std::move(a)), byref(bref), isArrayLength(isArrayLength) { mem.addRange(&type, sizeof(type)); } diff --git a/ir/irfuncty.h b/ir/irfuncty.h index 42adc5f4400..c541b3c8f76 100644 --- a/ir/irfuncty.h +++ b/ir/irfuncty.h @@ -60,6 +60,11 @@ struct IrFuncTyArg { * LLVM Type is a reference type! */ bool byref = false; + /** 'true' if this arg is the length part of an array parameter that is + broken into length + ptr + */ + bool isArrayLength; + /** Pointer to the ABIRewrite structure needed to rewrite LLVM ValueS * to match the final LLVM Type when passing arguments and getting * return values */ @@ -76,8 +81,8 @@ struct IrFuncTyArg { * @param byref Initial value for the 'byref' field. If true the initial * LLVM Type will be of DtoType(type->pointerTo()), instead * of just DtoType(type) */ - IrFuncTyArg(Type *t, bool byref); - IrFuncTyArg(Type *t, bool byref, llvm::AttrBuilder); + IrFuncTyArg(Type *t, bool byref, bool isArrayLength = false); + IrFuncTyArg(Type *t, bool byref, bool isArrayLength, llvm::AttrBuilder); IrFuncTyArg(const IrFuncTyArg &) = delete; ~IrFuncTyArg(); diff --git a/ir/irvar.h b/ir/irvar.h index ff6732cac6d..2619a867386 100644 --- a/ir/irvar.h +++ b/ir/irvar.h @@ -61,6 +61,8 @@ struct IrLocal : IrVar { struct IrParameter : IrLocal { explicit IrParameter(VarDeclaration *v) : IrLocal(v) {} IrFuncTyArg *arg = nullptr; + IrFuncTyArg *arg2 = nullptr; // companion pointer to an array length + llvm::Value *value2; bool isVthis = false; // true, if it is the 'this' parameter }; diff --git a/tests/codegen/restrict_slice.d b/tests/codegen/restrict_slice.d new file mode 100644 index 00000000000..94aa756fca5 --- /dev/null +++ b/tests/codegen/restrict_slice.d @@ -0,0 +1,12 @@ +// RUN: %ldc %s -c -output-ll | FileCheck +import ldc.attributes; + +//CHECK: define void @_D4testQfFAiZv({{i(64|32)}} %a_arg_len, {{i32\*|ptr}} noalias %a_arg_ptr) +//CHECK-NEXT: %a = alloca {{{i(64|32)}}, {{i32\*|ptr}}} align 8 +void test(@restrict int[] a) {} + +void use() +{ + int[4] x; + test(x[]); +}