Skip to content

Commit

Permalink
Merge pull request #132 from frasercrmck/fix-dbg-locs
Browse files Browse the repository at this point in the history
[compiler] Preserve debug sub-programs in wrapped kernels
  • Loading branch information
frasercrmck authored Sep 19, 2023
2 parents 7b6eeaf + 2daee1c commit f56bed7
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <compiler/utils/pass_functions.h>
#include <compiler/utils/scheduling.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm/IR/IRBuilder.h>
#include <refsi_g1_wi/refsi_wg_loop_pass.h>

Expand Down Expand Up @@ -155,10 +156,9 @@ PreservedAnalyses RefSiWGLoopPass::run(Module &M, ModuleAnalysisManager &AM) {
x, ir.CreateGEP(dstGroupIdTy, dstGroupId,
{i32_0, ir.getInt32(inner_dim)}));

auto ci = ir.CreateCall(function, args);
ci->setCallingConv(function->getCallingConv());
ci->setAttributes(
compiler::utils::getCopiedFunctionAttrs(*function));
compiler::utils::createCallToWrappedFunction(
*function, args, ir.GetInsertBlock(),
ir.GetInsertPoint());

auto *local_barrier = BI.getOrDeclareMuxBuiltin(
compiler::utils::eMuxBuiltinWorkGroupBarrier, M);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <compiler/utils/attributes.h>
#include <compiler/utils/pass_functions.h>
#include <compiler/utils/scheduling.h>
#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm/IR/IRBuilder.h>
#include <refsi_m1/refsi_wrapper_pass.h>

Expand Down Expand Up @@ -206,10 +207,9 @@ llvm::Function *addKernelWrapper(llvm::Module &M, llvm::Function &F) {
ArgIndex++;
}

auto CI = Builder.CreateCall(&F, Args);
CI->setCallingConv(F.getCallingConv());
// Copy over the attributes to the call site
CI->setAttributes(compiler::utils::getCopiedFunctionAttrs(F));
compiler::utils::createCallToWrappedFunction(
F, Args, Builder.GetInsertBlock(), Builder.GetInsertPoint());

Builder.CreateRetVoid();
return NewFunction;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <compiler/utils/metadata.h>
#include <compiler/utils/pass_functions.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/Module.h>
Expand Down Expand Up @@ -163,11 +164,8 @@ PreservedAnalyses compiler::ImageArgumentSubstitutionPass::run(
KernelF->getContext(), WrapperKernel->getAttributes().getFnAttrs(),
WrapperKernel->getAttributes().getRetAttrs(), WrapperParamAttrs));

auto *const CI = B.CreateCall(KernelF, Args);

CI->setCallingConv(KernelF->getCallingConv());
// Copy over the attributes to the call site
CI->setAttributes(compiler::utils::getCopiedFunctionAttrs(*KernelF));
utils::createCallToWrappedFunction(*KernelF, Args, B.GetInsertBlock(),
B.GetInsertPoint());

B.CreateRetVoid();

Expand Down
7 changes: 3 additions & 4 deletions modules/compiler/targets/host/source/AddEntryHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,9 @@ PreservedAnalyses AddEntryHookPass::run(Module &M, ModuleAnalysisManager &AM) {
x, ir.CreateGEP(dstGroupIdTy, dstGroupId,
{i32_0, ir.getInt32(vec_dim)}));

auto ci = ir.CreateCall(function, args);
ci->setCallingConv(function->getCallingConv());
ci->setAttributes(
compiler::utils::getCopiedFunctionAttrs(*function));
compiler::utils::createCallToWrappedFunction(
*function, args, ir.GetInsertBlock(),
ir.GetInsertPoint());

return blockx;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <compiler/utils/pass_functions.h>
#include <host/add_floating_point_control_pass.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/InlineAsm.h>
#include <llvm/IR/Instructions.h>
Expand Down Expand Up @@ -76,9 +77,8 @@ void configX86FP(Function &wrapper, Function &function) {
}

// call the function we are wrapping
auto *const ci = ir.CreateCall(&function, args);
ci->setCallingConv(function.getCallingConv());
ci->setAttributes(compiler::utils::getCopiedFunctionAttrs(function));
compiler::utils::createCallToWrappedFunction(
function, args, ir.GetInsertBlock(), ir.GetInsertPoint());

// reset the MXCSR to the original value
ir.CreateCall(ld_mxcsr, {bitcast_orig})
Expand Down
58 changes: 58 additions & 0 deletions modules/compiler/test/lit/passes/add-kernel-wrapper-dbg.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
; Copyright (C) Codeplay Software Limited
;
; Licensed under the Apache License, Version 2.0 (the "License") with LLVM
; Exceptions; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
; https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
; License for the specific language governing permissions and limitations
; under the License.
;
; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

; Check that wrapping kernels preserves the old DISubprogram and creates a new
; artificial one, which is used in a debug location for the wrapper call.
; RUN: muxc --passes add-kernel-wrapper,verify < %s | FileCheck %s

target triple = "spir64-unknown-unknown"
target datalayout = "e-p:64:64:64-m:e-i64:64-f80:128-n8:16:32:64-S128"

; CHECK: define internal spir_kernel void @foo(ptr addrspace(1) %x, ptr addrspace(1) %y)
; CHECK-SAME: [[ATTRS:#[0-9]+]] !dbg [[SP:\![0-9]+]] {
define spir_kernel void @foo(ptr addrspace(1) %x, ptr addrspace(1) %y) #0 !dbg !10 {
ret void
}

; CHECK: define spir_kernel void @foo.mux-kernel-wrapper(ptr %packed-args)
; CHECK-SAME: [[NEW_ATTRS:#[0-9]+]] !dbg [[NEW_SP:\![0-9]+]] !mux_scheduled_fn {{\![0-9]+}} {
; Check that when we call the original kernel we've attached a debug location.
; This is required by LLVM.
; CHECK: call spir_kernel void @foo({{.*}}) [[ATTRS]], !dbg [[LOC:\![0-9]+]]

; CHECK-DAG: attributes [[ATTRS]] = { alwaysinline }
; CHECK-DAG: attributes [[NEW_ATTRS]] = { nounwind "mux-base-fn-name"="foo" "mux-kernel"="entry-point" }
attributes #0 = { "mux-kernel"="entry-point" }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!6}

; The old kernel should not have an 'artificial' sub-program
; CHECK-DAG: [[SP]] = distinct !DISubprogram({{.*}}, flags: DIFlagPrototyped,
; The wrapper kernel should have an 'artificial' sub-program
; CHECK-DAG: [[NEW_SP]] = distinct !DISubprogram({{.*}}, flags: DIFlagArtificial | DIFlagPrototyped,
; The debug location should live in the wrapper's scope.
; CHECK-DAG: [[LOC]] = !DILocation(line: 0, scope: [[NEW_SP]])

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 16.0.4", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!1 = !DIFile(filename: "add-kernel-wrapper-dbg.ll", directory: "/tmp")
!2 = !DISubroutineType(cc: DW_CC_LLVM_OpenCLKernel, types: !3)
!3 = !{!4, !4}
!4 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64, dwarfAddressSpace: 1)
!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!6 = !{i32 2, !"Debug Info Version", i32 3}

!10 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !2, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
17 changes: 17 additions & 0 deletions modules/compiler/utils/include/compiler/utils/pass_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,23 @@ llvm::Function *createKernelWrapperFunction(llvm::Function &F,
llvm::StringRef Suffix,
llvm::StringRef OldSuffix = "");

/// @brief Creates a call to a a wrapped function
///
/// Sets the calling convention and call-site attributes to match the wrapped
/// function.
///
/// @param WrappedF the function to call
/// @param Args the list of arguments to pass to the call
/// @param BB the basic block into which to insert the call. May be null, in
/// which case the call is not inserted anywhere.
/// @param InsertPt the point in BB at which to insert the call
/// @param Name the name of the call instruction. May be empty.
/// @return The call instruction
llvm::CallInst *createCallToWrappedFunction(
llvm::Function &WrappedF, const llvm::SmallVectorImpl<llvm::Value *> &Args,
llvm::BasicBlock *BB, llvm::BasicBlock::iterator InsertPt,
llvm::StringRef Name = "");

/// @}
} // namespace utils
} // namespace compiler
Expand Down
5 changes: 2 additions & 3 deletions modules/compiler/utils/source/add_kernel_wrapper_pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,8 @@ PreservedAnalyses compiler::utils::AddKernelWrapperPass::run(
params.back()->setName(arg.getName());
}

auto ci = ir.CreateCall(&F, params);
ci->setCallingConv(F.getCallingConv());
ci->setAttributes(getCopiedFunctionAttrs(F));
createCallToWrappedFunction(F, params, ir.GetInsertBlock(),
ir.GetInsertPoint());

ir.CreateRetVoid();

Expand Down
44 changes: 40 additions & 4 deletions modules/compiler/utils/source/pass_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
#include <compiler/utils/pass_functions.h>
#include <llvm/IR/Attributes.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/Module.h>
#include <llvm/Transforms/Utils/Cloning.h>
#include <multi_llvm/llvm_version.h>
#include <multi_llvm/multi_llvm.h>
#include <multi_llvm/vector_type_helper.h>

Expand Down Expand Up @@ -654,10 +656,12 @@ static llvm::Function *createKernelWrapperFunctionImpl(
// copied it over to the new one.
dropIsKernel(F);

// move debug info for function over
NewFunction.setSubprogram(F.getSubprogram());

F.setSubprogram(nullptr);
// copy debug info for function over
if (auto *SP = F.getSubprogram()) {
llvm::DIBuilder DIB(*F.getParent());
auto *NewSP = DIB.createArtificialSubprogram(SP);
NewFunction.setSubprogram(NewSP);
}

// set the function to always inline: 'noinline' takes precedence, though
if (!F.hasFnAttribute(llvm::Attribute::NoInline)) {
Expand Down Expand Up @@ -705,5 +709,37 @@ llvm::Function *createKernelWrapperFunction(
return createKernelWrapperFunctionImpl(F, *NewFunction, Suffix, OldSuffix);
}

llvm::CallInst *createCallToWrappedFunction(
llvm::Function &WrappedF, const llvm::SmallVectorImpl<llvm::Value *> &Args,
llvm::BasicBlock *BB, llvm::BasicBlock::iterator InsertPt,
llvm::StringRef Name) {
auto *const CI =
llvm::CallInst::Create(WrappedF.getFunctionType(), &WrappedF, Args);

CI->setName(Name);
CI->setCallingConv(WrappedF.getCallingConv());
CI->setAttributes(getCopiedFunctionAttrs(WrappedF));

if (BB) {
#if LLVM_VERSION_GREATER(15, 0)
CI->insertInto(BB, InsertPt);
#else
BB->getInstList().insert(InsertPt, CI);
#endif

if (auto *const ParentF = BB->getParent()) {
// An inlinable function call in a function with debug info *must* be
// given a debug location.
if (auto *const SP = ParentF->getSubprogram()) {
auto *const DbgLoc = llvm::DILocation::get(ParentF->getContext(),
/*line*/ 0, /*col*/ 0, SP);
CI->setDebugLoc(DbgLoc);
}
}
}

return CI;
}

} // namespace utils
} // namespace compiler
16 changes: 5 additions & 11 deletions modules/compiler/utils/source/work_item_loops_pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1231,25 +1231,19 @@ Function *compiler::utils::WorkItemLoopsPass::makeWrapperFunction(

LLVMContext &context = M.getContext();

// Cache the old subprogram as createKernelWrapperFunction will overwrite
// the reference subprogram.
auto *const OldSP = refF.getSubprogram();

Function *new_wrapper =
createKernelWrapperFunction(mainF, ".mux-barrier-wrapper");

new_wrapper->setName(baseName + ".mux-barrier-wrapper");
// Ensure the base name is recorded
setBaseFnName(*new_wrapper, baseName);

// construct an artificial subprogram and debug location for this wrapper
// kernel.
// An inlinable function call in a function with debug info *must* be given
// a debug location.
DILocation *wrapperDbgLoc = nullptr;
if (OldSP) {
DIBuilder DIB(M);
auto *NewSP = DIB.createArtificialSubprogram(OldSP);
new_wrapper->setSubprogram(NewSP);
wrapperDbgLoc = DILocation::get(context, 0, 0, NewSP);
if (auto *const SP = new_wrapper->getSubprogram()) {
wrapperDbgLoc = DILocation::get(context, /*line*/ 0, /*col*/ 0,
new_wrapper->getSubprogram());
}

IRBuilder<> entryIR(BasicBlock::Create(context, "entry", new_wrapper));
Expand Down

0 comments on commit f56bed7

Please sign in to comment.