Skip to content

Commit 8c1098c

Browse files
committed
Introduce new ThreadLocalLowering pass
This pass will make it possible to use wasm threadlocals in genericjs, by creating and calling a function in wasm that will calculate the address in memory of the threadlocal.
1 parent 2cf3e5f commit 8c1098c

File tree

6 files changed

+171
-0
lines changed

6 files changed

+171
-0
lines changed

llvm/include/llvm/Cheerp/PassRegistry.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "llvm/Cheerp/CallConstructors.h"
4242
#include "llvm/Cheerp/CommandLine.h"
4343
#include "llvm/Cheerp/CheerpLowerAtomic.h"
44+
#include "llvm/Cheerp/ThreadLocalLowering.h"
4445

4546
namespace cheerp {
4647

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===-- ThreadLocalLowering.h - Cheerp helper -------------------------===//
2+
//
3+
// Cheerp: The C++ compiler for the Web
4+
//
5+
// This file is distributed under the Apache License v2.0 with LLVM Exceptions.
6+
// See LICENSE.TXT for details.
7+
//
8+
// Copyright 2024-2025 Leaning Technologies
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
#ifndef CHEERP_THREAD_LOCAL_LOWERING_H
13+
#define CHEERP_THREAD_LOCAL_LOWERING_H
14+
15+
#include "llvm/IR/PassManager.h"
16+
#include "llvm/Cheerp/GlobalDepsAnalyzer.h"
17+
18+
namespace cheerp{
19+
20+
using namespace llvm;
21+
22+
class ThreadLocalLoweringInnerPass: public PassInfoMixin<ThreadLocalLoweringInnerPass> {
23+
GlobalDepsAnalyzer& GDA;
24+
public:
25+
ThreadLocalLoweringInnerPass(GlobalDepsAnalyzer& GDA) : GDA(GDA)
26+
{
27+
}
28+
PreservedAnalyses run(Function& F, FunctionAnalysisManager& FAM);
29+
static bool isRequired() { return true;}
30+
};
31+
32+
class ThreadLocalLoweringPass: public PassInfoMixin<ThreadLocalLoweringPass> {
33+
public:
34+
PreservedAnalyses run(Module& M, ModuleAnalysisManager& MAM);
35+
static bool isRequired() { return true;}
36+
};
37+
38+
}
39+
40+
#endif

llvm/lib/CheerpUtils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ add_llvm_component_library(LLVMCheerpUtils
3535
FinalizeMemoryInfo.cpp
3636
CheerpLowerAtomic.cpp
3737
JsExport.cpp
38+
ThreadLocalLowering.cpp
3839
)
3940

4041
add_dependencies(LLVMCheerpUtils intrinsics_gen)

llvm/lib/CheerpUtils/PointerAnalyzer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ PointerKindWrapper& PointerUsageVisitor::visitValue(PointerKindWrapper& ret, con
672672
case Intrinsic::invariant_start:
673673
return CacheAndReturn(visitValue(ret, intrinsic->getArgOperand(1), /*first*/ false));
674674
case Intrinsic::stacksave:
675+
case Intrinsic::threadlocal_address:
675676
return CacheAndReturn(ret = PointerKindWrapper(RAW));
676677
case Intrinsic::invariant_end:
677678
case Intrinsic::vastart:
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//===-- ThreadLocalLowering.cpp - Cheerp helper -------------------------===//
2+
//
3+
// Cheerp: The C++ compiler for the Web
4+
//
5+
// This file is distributed under the Apache License v2.0 with LLVM Exceptions.
6+
// See LICENSE.TXT for details.
7+
//
8+
// Copyright 2024-2025 Leaning Technologies
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
#include "llvm/Cheerp/ThreadLocalLowering.h"
13+
#include "llvm/Cheerp/GlobalDepsAnalyzer.h"
14+
#include "llvm/Cheerp/LinearMemoryHelper.h"
15+
#include "llvm/Cheerp/InvokeWrapping.h"
16+
#include "llvm/Cheerp/Registerize.h"
17+
#include "llvm/IR/Instruction.h"
18+
#include "llvm/IR/IntrinsicInst.h"
19+
#include "llvm/IR/IRBuilder.h"
20+
21+
using namespace llvm;
22+
23+
namespace cheerp
24+
{
25+
26+
static Function* getOrCreateThreadLocalWrapper(Module* M, GlobalDepsAnalyzer& GDA)
27+
{
28+
Type* i8Ty = IntegerType::getInt8Ty(M->getContext());
29+
Type* i8PtrTy = PointerType::get(i8Ty, 0);
30+
Type* i32Ty = IntegerType::getInt32Ty(M->getContext());
31+
Type* argTy[] = {i32Ty};
32+
FunctionType* fTy = FunctionType::get(i8PtrTy,ArrayRef<Type*>(argTy, 1), false);
33+
Function* wrapper = cast<Function>(M->getOrInsertFunction("__getThreadLocalAddress", fTy).getCallee());
34+
if (!wrapper->empty())
35+
return wrapper;
36+
37+
BasicBlock* entry = BasicBlock::Create(M->getContext(),"entry", wrapper);
38+
IRBuilder<> Builder(entry);
39+
// Get the thread local address.
40+
Function* threadPointerIntrinsic = Intrinsic::getDeclaration(M, Intrinsic::cheerp_get_thread_pointer);
41+
Value* threadPointer = Builder.CreateCall(threadPointerIntrinsic);
42+
// Add the offset argument
43+
Value* offset = wrapper->getArg(0);
44+
Value* address = Builder.CreateAdd(threadPointer, offset);
45+
// Bitcast to a pointer
46+
address = Builder.CreateIntToPtr(address, i8PtrTy);
47+
Builder.CreateRet(address);
48+
49+
wrapper->setSection("asmjs");
50+
GDA.insertAsmJSExport(wrapper);
51+
return wrapper;
52+
}
53+
54+
bool replaceThreadLocalIntrinsicWithFunction(Function& F, GlobalDepsAnalyzer& GDA)
55+
{
56+
Module* M = F.getParent();
57+
bool changed = false;
58+
SmallVector<Instruction*, 8> deleteList;
59+
60+
for (BasicBlock& BB: F)
61+
{
62+
for (Instruction& I: BB)
63+
{
64+
if (isa<IntrinsicInst>(I))
65+
{
66+
IntrinsicInst& II = cast<IntrinsicInst>(I);
67+
Intrinsic::ID intrId = II.getIntrinsicID();
68+
if (intrId == Intrinsic::threadlocal_address)
69+
{
70+
IRBuilder<> Builder(&II);
71+
// Replace call to intrinsic with function
72+
// 1. Use an intrinsic that will be the offset for the threadlocal.
73+
Type* argTy[] = {II.getOperand(0)->getType()};
74+
Function* offsetIntrinsic = Intrinsic::getDeclaration(M, Intrinsic::cheerp_get_threadlocal_offset, argTy);
75+
Value* offset = Builder.CreateCall(offsetIntrinsic, II.getOperand(0));
76+
// 2. Pass this offset to the wasm function that will calculate the address from the thread pointer.
77+
Function* newFunc = getOrCreateThreadLocalWrapper(M, GDA);
78+
Value* newCall = Builder.CreateCall(newFunc, offset);
79+
// 3. Bitcast return code from this function to required type.
80+
Type* origType = II.getType();
81+
if (origType != newCall->getType())
82+
newCall = Builder.CreateBitCast(newCall, origType);
83+
I.replaceAllUsesWith(newCall);
84+
deleteList.push_back(&I);
85+
changed = true;
86+
}
87+
}
88+
}
89+
}
90+
for (Instruction* I: deleteList)
91+
I->eraseFromParent();
92+
return changed;
93+
}
94+
95+
PreservedAnalyses ThreadLocalLoweringInnerPass::run(Function& F, FunctionAnalysisManager& FAM)
96+
{
97+
if (F.getSection() == "asmjs")
98+
return PreservedAnalyses::all();
99+
100+
// Find calls to threadlocal.address intrinsic, replace with calls to function.
101+
bool changed = replaceThreadLocalIntrinsicWithFunction(F, GDA);
102+
if (!changed)
103+
return PreservedAnalyses::all();
104+
PreservedAnalyses PA;
105+
PA.preserve<PointerAnalysis>();
106+
PA.preserve<RegisterizeAnalysis>();
107+
PA.preserve<GlobalDepsAnalysis>();
108+
PA.preserve<DominatorTreeAnalysis>();
109+
PA.preserve<InvokeWrappingAnalysis>();
110+
return PA;
111+
}
112+
113+
PreservedAnalyses ThreadLocalLoweringPass::run(Module& M, ModuleAnalysisManager& MAM)
114+
{
115+
FunctionPassManager FPM;
116+
117+
GlobalDepsAnalyzer& GDA = MAM.getResult<GlobalDepsAnalysis>(M);
118+
FPM.addPass(ThreadLocalLoweringInnerPass(GDA));
119+
120+
ModulePassManager MPM;
121+
122+
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
123+
PreservedAnalyses PA = MPM.run(M, MAM);
124+
return PA;
125+
}
126+
127+
}

llvm/lib/Target/WebAssembly/CheerpWritePass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ bool CheerpWritePass::runOnModule(Module& M)
241241
MPM.addPass(cheerp::InvokeWrappingPass());
242242
if (isWasmTarget)
243243
MPM.addPass(cheerp::AllocaLoweringPass());
244+
MPM.addPass(cheerp::ThreadLocalLoweringPass());
244245
MPM.addPass(cheerp::FFIWrappingPass());
245246
MPM.addPass(createModuleToFunctionPassAdaptor(cheerp::FixIrreducibleControlFlowPass()));
246247
MPM.addPass(createModuleToFunctionPassAdaptor(cheerp::PointerArithmeticToArrayIndexingPass()));

0 commit comments

Comments
 (0)