1 /*
2 * Copyright (c) 2023 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
21 #include "compiler/optimizer/ir/basicblock.h"
22
23 #include <llvm/IR/MDBuilder.h>
24 #include <llvm/ADT/SmallVector.h>
25 #include <llvm/Transforms/Utils/BasicBlockUtils.h>
26
27 namespace panda::llvmbackend::gc_barriers {
28
EmitPreWRB(llvm::IRBuilder<> * builder,llvm::Value * mem,bool isVolatileMem,llvm::BasicBlock * outBb,LLVMArkInterface * arkInterface,llvm::Value * threadRegValue)29 void EmitPreWRB(llvm::IRBuilder<> *builder, llvm::Value *mem, bool isVolatileMem, llvm::BasicBlock *outBb,
30 LLVMArkInterface *arkInterface, llvm::Value *threadRegValue)
31 {
32 auto func = builder->GetInsertBlock()->getParent();
33 auto module = func->getParent();
34 auto &ctx = module->getContext();
35 auto initialBb = builder->GetInsertBlock();
36
37 auto createUniqBasicBlockName = [&initialBb](const std::string &suffix) {
38 return panda::llvmbackend::LLVMArkInterface::GetUniqueBasicBlockName(initialBb->getName().str(), suffix);
39 };
40 auto createBasicBlock = [&ctx, &initialBb, &createUniqBasicBlockName](const std::string &suffix) {
41 auto name = createUniqBasicBlockName(suffix);
42 auto funcIbb = initialBb->getParent();
43 return llvm::BasicBlock::Create(ctx, name, funcIbb);
44 };
45
46 auto loadValueBb = createBasicBlock("pre_wrb_load_value");
47 auto callRuntimeBb = createBasicBlock("pre_wrb_call_runtime");
48 auto threadStructPtr = builder->CreateIntToPtr(threadRegValue, builder->getPtrTy());
49 auto entrypointOffset = arkInterface->GetTlsPreWrbEntrypointOffset();
50 auto entrypointPtr = builder->CreateConstInBoundsGEP1_32(builder->getInt8Ty(), threadStructPtr, entrypointOffset);
51
52 // Check if entrypoint is null
53 auto entrypoint =
54 builder->CreateLoad(builder->getPtrTy(), entrypointPtr, "__panda_entrypoint_PreWrbFuncNoBridge_addr");
55 auto hasEntrypoint = builder->CreateIsNotNull(entrypoint);
56 builder->CreateCondBr(hasEntrypoint, loadValueBb, outBb);
57
58 // Load old value
59 builder->SetInsertPoint(loadValueBb);
60
61 // See LLVMEntry::EmitLoad
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 int32Ty = builder->getInt32Ty();
90 auto threadRegPtr = builder->CreateIntToPtr(threadRegValue, ptrTy);
91 auto addr = builder->CreateConstInBoundsGEP1_64(builder->getInt8Ty(), threadRegPtr, tlsOffset);
92 auto callee = builder->CreateLoad(ptrTy, addr, "post_wrb_one_object_addr");
93
94 if (arkInterface->IsArm64()) {
95 // Arm64 Irtoc, 4 params (add thread)
96 auto funcTy = llvm::FunctionType::get(builder->getVoidTy(), {gcPtrTy, int32Ty, gcPtrTy, ptrTy}, false);
97 auto call = builder->CreateCall(funcTy, callee, {mem, offset, value, threadRegPtr});
98 call->setCallingConv(llvm::CallingConv::ArkFast3);
99 return;
100 }
101 // X86_64 Irtoc, 5 params (add thread, fp)
102 ASSERT(frameRegValue != nullptr);
103 auto funcTy = llvm::FunctionType::get(builder->getVoidTy(), {gcPtrTy, int32Ty, gcPtrTy, ptrTy, ptrTy}, false);
104 auto frameRegPtr = builder->CreateIntToPtr(frameRegValue, ptrTy);
105 auto call = builder->CreateCall(funcTy, callee, {mem, offset, value, threadRegPtr, frameRegPtr});
106 call->setCallingConv(llvm::CallingConv::ArkFast3);
107 }
108
109 } // namespace panda::llvmbackend::gc_barriers
110