@@ -314,21 +314,81 @@ HipPrintfToOpenCLPrintfPass::getOrCreateStrLiteralArg(const std::string &Str,
314314Function *HipPrintfToOpenCLPrintfPass::getOrCreatePrintStringF () {
315315
316316 if (GlobalValue *OldPrintStrF =
317- M_ ->getNamedValue (ORIG_PRINT_STRING_FUNC_NAME ))
318- return cast<Function>(OldPrintStrF);
317+ M_ ->getNamedValue (ORIG_PRINT_STRING_FUNC_NAME )) {
318+ auto *Existing = cast<Function>(OldPrintStrF);
319+ if (!Existing->isDeclaration ())
320+ return Existing;
321+ // Declaration exists (e.g. inserted on a prior call before we got
322+ // to define a body); fall through and define the body now.
323+ }
319324
320- auto *Int8Ty = IntegerType::get (M_ ->getContext (), 8 );
325+ auto &Ctx = M_ ->getContext ();
326+ auto *Int8Ty = IntegerType::get (Ctx, 8 );
327+ auto *Int32Ty = IntegerType::get (Ctx, 32 );
328+ auto *VoidTy = Type::getVoidTy (Ctx);
321329 PointerType *GenericCStrArgT =
322330 PointerType::get (Int8Ty, SPIRV_OPENCL_GENERIC_AS );
331+ PointerType *ConstStrPtrT =
332+ PointerType::get (Int8Ty, SPIRV_OPENCL_CONSTANT_AS );
323333
324- FunctionType *PrintStrFTy = FunctionType::get (
325- Type::getVoidTy (M_ ->getContext ()), {GenericCStrArgT}, false );
326-
327- FunctionCallee PrintStrF =
328- M_ ->getOrInsertFunction (ORIG_PRINT_STRING_FUNC_NAME , PrintStrFTy);
329- cast<Function>(PrintStrF.getCallee ())
330- ->setCallingConv (llvm::CallingConv::SPIR_FUNC );
331- return cast<Function>(PrintStrF.getCallee ());
334+ FunctionType *PrintStrFTy =
335+ FunctionType::get (VoidTy, {GenericCStrArgT}, false );
336+ Function *F = cast<Function>(
337+ M_ ->getOrInsertFunction (ORIG_PRINT_STRING_FUNC_NAME , PrintStrFTy)
338+ .getCallee ());
339+ F->setCallingConv (llvm::CallingConv::SPIR_FUNC );
340+ F->setLinkage (llvm::GlobalValue::InternalLinkage);
341+
342+ // Define the body inline so the kernel module is self-contained for %s
343+ // printf support. The historical implementation in bitcode/_cl_print_str.cl
344+ // required `static __attribute__((used))`, which forced an `@llvm.used`
345+ // entry into hipspv.bc. That collided with `@llvm.used` from HIP TUs
346+ // (different element address space) at `-mlink-builtin-bitcode` time and
347+ // broke any HIP code that also uses `__attribute__((used))` (rocThrust).
348+ //
349+ // Equivalent C:
350+ // void _cl_print_str(__generic const char *S) {
351+ // if (S == 0) return;
352+ // unsigned Pos = 0;
353+ // char C;
354+ // while ((C = S[Pos]) != 0) { printf("%c", C); ++Pos; }
355+ // }
356+ BasicBlock *Entry = BasicBlock::Create (Ctx, " entry" , F);
357+ BasicBlock *Loop = BasicBlock::Create (Ctx, " loop" , F);
358+ BasicBlock *Body = BasicBlock::Create (Ctx, " body" , F);
359+ BasicBlock *Exit = BasicBlock::Create (Ctx, " exit" , F);
360+
361+ Argument *S = F->getArg (0 );
362+
363+ FunctionType *PrintfTy =
364+ FunctionType::get (Int32Ty, {ConstStrPtrT}, /* isVarArg=*/ true );
365+ Function *Printf = cast<Function>(
366+ M_ ->getOrInsertFunction (" printf" , PrintfTy).getCallee ());
367+
368+ IRBuilder<> B (Entry);
369+ Value *IsNull = B.CreateICmpEQ (S, ConstantPointerNull::get (GenericCStrArgT));
370+ B.CreateCondBr (IsNull, Exit, Loop);
371+
372+ B.SetInsertPoint (Loop);
373+ PHINode *Pos = B.CreatePHI (Int32Ty, 2 );
374+ Pos->addIncoming (ConstantInt::get (Int32Ty, 0 ), Entry);
375+ Value *CharPtr = B.CreateGEP (Int8Ty, S, Pos);
376+ Value *C = B.CreateLoad (Int8Ty, CharPtr);
377+ Value *IsZero = B.CreateICmpEQ (C, ConstantInt::get (Int8Ty, 0 ));
378+ B.CreateCondBr (IsZero, Exit, Body);
379+
380+ B.SetInsertPoint (Body);
381+ Constant *PercentC = getOrCreateStrLiteralArg (" %c" , B);
382+ CallInst *PrintfCall = B.CreateCall (Printf, {PercentC, C});
383+ PrintfCall->setCallingConv (llvm::CallingConv::SPIR_FUNC );
384+ Value *PosNext = B.CreateAdd (Pos, ConstantInt::get (Int32Ty, 1 ));
385+ Pos->addIncoming (PosNext, Body);
386+ B.CreateBr (Loop);
387+
388+ B.SetInsertPoint (Exit);
389+ B.CreateRetVoid ();
390+
391+ return F;
332392}
333393
334394// Get called function from 'CI' call or return nullptr the call is indirect.
0 commit comments