1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "gc_barriers.h"
17
18 #include "llvm_ark_interface.h"
19 #include "metadata.h"
20 #include "transforms/builtins.h"
21 #include "transforms/runtime_calls.h"
22
23 #include "compiler/optimizer/ir/basicblock.h"
24
25 #include <llvm/IR/MDBuilder.h>
26 #include <llvm/ADT/SmallVector.h>
27 #include <llvm/Transforms/Utils/BasicBlockUtils.h>
28
29 namespace ark::llvmbackend::gc_barriers {
30
EmitPreWRB(llvm::IRBuilder<> * builder,llvm::Value * mem,bool isVolatileMem,llvm::BasicBlock * outBb,LLVMArkInterface * arkInterface,llvm::Value * threadRegValue)31 void EmitPreWRB(llvm::IRBuilder<> *builder, llvm::Value *mem, bool isVolatileMem, llvm::BasicBlock *outBb,
32 LLVMArkInterface *arkInterface, llvm::Value *threadRegValue)
33 {
34 auto func = builder->GetInsertBlock()->getParent();
35 auto module = func->getParent();
36 auto &ctx = module->getContext();
37 auto initialBb = builder->GetInsertBlock();
38
39 auto createUniqBasicBlockName = [&initialBb](const std::string &suffix) {
40 return ark::llvmbackend::LLVMArkInterface::GetUniqueBasicBlockName(initialBb->getName().str(), suffix);
41 };
42 auto createBasicBlock = [&ctx, &initialBb, &createUniqBasicBlockName](const std::string &suffix) {
43 auto name = createUniqBasicBlockName(suffix);
44 auto funcIbb = initialBb->getParent();
45 return llvm::BasicBlock::Create(ctx, name, funcIbb);
46 };
47
48 auto loadValueBb = createBasicBlock("pre_wrb_load_value");
49 auto callRuntimeBb = createBasicBlock("pre_wrb_call_runtime");
50 auto threadStructPtr = builder->CreateIntToPtr(threadRegValue, builder->getPtrTy());
51 auto entrypointOffset = arkInterface->GetTlsPreWrbEntrypointOffset();
52 auto entrypointPtr = builder->CreateConstInBoundsGEP1_32(builder->getInt8Ty(), threadStructPtr, entrypointOffset);
53
54 // Check if entrypoint is null
55 auto entrypoint =
56 builder->CreateLoad(builder->getPtrTy(), entrypointPtr, "__panda_entrypoint_PreWrbFuncNoBridge_addr");
57 auto hasEntrypoint = builder->CreateIsNotNull(entrypoint);
58 builder->CreateCondBr(hasEntrypoint, loadValueBb, outBb);
59
60 // Load old value, similar to LLVMIrConstructor::CreateLoadWithOrdering
61 builder->SetInsertPoint(loadValueBb);
62 auto load = builder->CreateLoad(builder->getPtrTy(LLVMArkInterface::GC_ADDR_SPACE), mem);
63 if (isVolatileMem) {
64 auto alignment = module->getDataLayout().getPrefTypeAlignment(load->getType());
65 load->setOrdering(LLVMArkInterface::VOLATILE_ORDER);
66 load->setAlignment(llvm::Align(alignment));
67 }
68 auto objectIsNull = builder->CreateIsNotNull(load);
69 builder->CreateCondBr(objectIsNull, callRuntimeBb, outBb);
70
71 // Call Runtime
72 builder->SetInsertPoint(callRuntimeBb);
73 static constexpr auto VAR_ARGS = true;
74 auto functionType =
75 llvm::FunctionType::get(builder->getVoidTy(), {builder->getPtrTy(LLVMArkInterface::GC_ADDR_SPACE)}, !VAR_ARGS);
76 builder->CreateCall(functionType, entrypoint, {load});
77 builder->CreateBr(outBb);
78
79 builder->SetInsertPoint(outBb);
80 }
81
EmitPostWRB(llvm::IRBuilder<> * builder,llvm::Value * mem,llvm::Value * offset,llvm::Value * value,LLVMArkInterface * arkInterface,llvm::Value * threadRegValue,llvm::Value * frameRegValue)82 void EmitPostWRB(llvm::IRBuilder<> *builder, llvm::Value *mem, llvm::Value *offset, llvm::Value *value,
83 LLVMArkInterface *arkInterface, llvm::Value *threadRegValue, llvm::Value *frameRegValue)
84 {
85 auto tlsOffset = arkInterface->GetManagedThreadPostWrbOneObjectOffset();
86
87 auto gcPtrTy = builder->getPtrTy(LLVMArkInterface::GC_ADDR_SPACE);
88 auto ptrTy = builder->getPtrTy();
89 auto memTy = mem->getType();
90 auto int32Ty = builder->getInt32Ty();
91 auto threadRegPtr = builder->CreateIntToPtr(threadRegValue, ptrTy);
92 auto addr = builder->CreateConstInBoundsGEP1_64(builder->getInt8Ty(), threadRegPtr, tlsOffset);
93 auto callee = builder->CreateLoad(ptrTy, addr, "post_wrb_one_object_addr");
94
95 ASSERT(mem->getType()->isPointerTy());
96 ASSERT(value->getType()->isPointerTy() &&
97 value->getType()->getPointerAddressSpace() == LLVMArkInterface::GC_ADDR_SPACE);
98
99 if (!arkInterface->IsIrtocMode()) {
100 // LLVM AOT, only 3 parameters
101 auto funcTy = llvm::FunctionType::get(builder->getVoidTy(), {memTy, int32Ty, gcPtrTy}, false);
102 auto call = builder->CreateCall(funcTy, callee, {mem, offset, value});
103 call->setCallingConv(llvm::CallingConv::ArkFast3);
104 return;
105 }
106 if (arkInterface->IsArm64()) {
107 // Arm64 Irtoc, 4 params (add thread)
108 auto funcTy = llvm::FunctionType::get(builder->getVoidTy(), {memTy, int32Ty, gcPtrTy, ptrTy}, false);
109 auto call = builder->CreateCall(funcTy, callee, {mem, offset, value, threadRegPtr});
110 call->setCallingConv(llvm::CallingConv::ArkFast3);
111 return;
112 }
113 // X86_64 Irtoc, 5 params (add thread, fp)
114 ASSERT(frameRegValue != nullptr);
115 auto funcTy = llvm::FunctionType::get(builder->getVoidTy(), {memTy, int32Ty, gcPtrTy, ptrTy, ptrTy}, false);
116 auto frameRegPtr = builder->CreateIntToPtr(frameRegValue, ptrTy);
117 auto call = builder->CreateCall(funcTy, callee, {mem, offset, value, threadRegPtr, frameRegPtr});
118 call->setCallingConv(llvm::CallingConv::ArkFast3);
119 }
120
121 } // namespace ark::llvmbackend::gc_barriers
122