• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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 "codegen_boundary.h"
17 #include "utils/cframe_layout.h"
18 
19 namespace ark::compiler {
20 
PushStackRegister(Encoder * encoder,Target target,Reg threadReg,size_t tlsFrameOffset)21 static void PushStackRegister(Encoder *encoder, Target target, Reg threadReg, size_t tlsFrameOffset)
22 {
23     static constexpr ssize_t FP_OFFSET = 2;
24     const auto wordSize = static_cast<ssize_t>(target.WordSize());
25     ASSERT(sizeof(FrameBridgeKind) <= static_cast<size_t>(wordSize));
26     constexpr ssize_t MAX_ALLOWED = std::numeric_limits<ssize_t>::max() / FP_OFFSET;
27     if (wordSize > MAX_ALLOWED) {
28         LOG(FATAL, IRTOC) << "WordSize too large for signed offset";
29         return;
30     }
31     encoder->EncodeSti(FrameBridgeKind::COMPILED_CODE_TO_INTERPRETER, wordSize,
32                        MemRef(target.GetStackReg(), -wordSize));
33     encoder->EncodeStr(target.GetFrameReg(), MemRef(target.GetStackReg(), -FP_OFFSET * wordSize));
34 
35     {
36         ScopedTmpReg tmp(encoder);
37         encoder->EncodeSub(tmp, target.GetStackReg(), Imm(2U * target.WordSize()));
38         encoder->EncodeStr(tmp, MemRef(threadReg, tlsFrameOffset));
39     }
40 }
41 
PushLinkAndStackRegister(Encoder * encoder,Target target,Reg threadReg,size_t tlsFrameOffset)42 static void PushLinkAndStackRegister(Encoder *encoder, Target target, Reg threadReg, size_t tlsFrameOffset)
43 {
44     ScopedTmpReg tmp(encoder);
45     static constexpr ssize_t FP_OFFSET = 3;
46     static constexpr ssize_t LR_OFFSET = 2;
47     encoder->EncodeMov(tmp, Imm(FrameBridgeKind::COMPILED_CODE_TO_INTERPRETER));
48     encoder->EncodeStp(tmp, target.GetLinkReg(), MemRef(target.GetStackReg(), -LR_OFFSET * target.WordSize()));
49     encoder->EncodeStr(target.GetFrameReg(), MemRef(target.GetStackReg(), -FP_OFFSET * target.WordSize()));
50 
51     encoder->EncodeSub(target.GetLinkReg(), target.GetStackReg(), Imm(FP_OFFSET * target.WordSize()));
52     encoder->EncodeStr(target.GetLinkReg(), MemRef(threadReg, tlsFrameOffset));
53 }
54 
GeneratePrologue()55 void CodegenBoundary::GeneratePrologue()
56 {
57     SCOPED_DISASM_STR(this, "Boundary Prologue");
58     auto encoder = GetEncoder();
59     auto frame = GetFrameInfo();
60     auto target = GetTarget();
61     auto threadReg = ThreadReg();
62     auto tlsFrameOffset = GetRuntime()->GetTlsFrameOffset(GetArch());
63 
64     if (target.SupportLinkReg()) {
65         PushLinkAndStackRegister(encoder, target, threadReg, tlsFrameOffset);
66     } else {
67         PushStackRegister(encoder, target, threadReg, tlsFrameOffset);
68     }
69 
70     encoder->EncodeSub(GetTarget().GetStackReg(), GetTarget().GetStackReg(), Imm(frame->GetFrameSize()));
71 
72     auto calleeRegs =
73         GetCalleeRegsMask(GetArch(), false) & ~GetTarget().GetTempRegsMask().to_ulong() & ~ThreadReg().GetId();
74     auto calleeVregs = GetCalleeRegsMask(GetArch(), true) & ~GetTarget().GetTempVRegsMask().to_ulong();
75     auto callerRegs = GetCallerRegsMask(GetArch(), false) & ~GetTarget().GetTempRegsMask().to_ulong();
76     auto callerVregs = GetCallerRegsMask(GetArch(), true) & ~GetTarget().GetTempVRegsMask().to_ulong();
77 
78     Reg base = GetTarget().GetFrameReg();
79     auto fl = GetFrameLayout();
80     {
81         SCOPED_DISASM_STR(this, "Save caller registers");
82         ssize_t offset = fl.GetOffset<CFrameLayout::OffsetOrigin::FP, CFrameLayout::OffsetUnit::SLOTS>(
83             CFrameLayout::GetStackStartSlot() + fl.GetCallerLastSlot(false));
84         encoder->SaveRegisters(callerRegs, false, -offset, base, GetCallerRegsMask(GetArch(), false));
85         offset = fl.GetOffset<CFrameLayout::OffsetOrigin::FP, CFrameLayout::OffsetUnit::SLOTS>(
86             CFrameLayout::GetStackStartSlot() + fl.GetCallerLastSlot(true));
87         encoder->SaveRegisters(callerVregs, true, -offset, base, GetCallerRegsMask(GetArch(), true));
88     }
89     {
90         SCOPED_DISASM_STR(this, "Save callee registers");
91         base = frame->GetCalleesRelativeFp() ? GetTarget().GetFrameReg() : GetTarget().GetStackReg();
92         encoder->SaveRegisters(calleeRegs, false, frame->GetCalleesOffset(), base, GetCalleeRegsMask(GetArch(), false));
93         encoder->SaveRegisters(calleeVregs, true, frame->GetFpCalleesOffset(), base,
94                                GetCalleeRegsMask(GetArch(), true));
95     }
96 }
97 
GenerateEpilogue()98 void CodegenBoundary::GenerateEpilogue()
99 {
100     SCOPED_DISASM_STR(this, "Boundary Epilogue");
101     RemoveBoundaryFrame(GetGraph()->GetEndBlock());
102     GetEncoder()->EncodeReturn();
103 }
104 
CreateFrameInfo()105 void CodegenBoundary::CreateFrameInfo()
106 {
107     auto frame = GetGraph()->GetLocalAllocator()->New<FrameInfo>(
108         FrameInfo::PositionedCallers::Encode(false) | FrameInfo::PositionedCallees::Encode(true) |
109         FrameInfo::CallersRelativeFp::Encode(false) | FrameInfo::CalleesRelativeFp::Encode(false));
110     auto target = Target(GetGraph()->GetArch());
111     size_t spillsCount = GetGraph()->GetStackSlotsCount();
112     size_t padding = 0;
113     size_t frameSize =
114         (CFrameLayout::HEADER_SIZE - (target.SupportLinkReg() ? 0 : 1) + GetCalleeRegsCount(target.GetArch(), false) +
115          GetCalleeRegsCount(target.GetArch(), true) + GetCallerRegsCount(target.GetArch(), false) +
116          GetCallerRegsCount(target.GetArch(), true) + spillsCount) *
117         target.WordSize();
118     if (target.SupportLinkReg()) {
119         padding = RoundUp(frameSize, target.GetSpAlignment()) - frameSize;
120     } else {
121         if ((frameSize % target.GetSpAlignment()) == 0) {
122             padding = target.GetSpAlignment() - target.WordSize();
123         }
124     }
125     CHECK_EQ(padding % target.WordSize(), 0U);
126     spillsCount += padding / target.WordSize();
127     frameSize += padding;
128     if (target.SupportLinkReg()) {
129         CHECK_EQ(frameSize % target.GetSpAlignment(), 0U);
130     } else {
131         CHECK_EQ(frameSize % target.GetSpAlignment(), target.GetSpAlignment() - target.WordSize());
132     }
133 
134     auto offset = static_cast<ssize_t>(spillsCount);
135     frame->SetFpCallersOffset(offset);
136     offset += helpers::ToSigned(GetCallerRegsCount(target.GetArch(), true));
137     frame->SetCallersOffset(offset);
138     offset += helpers::ToSigned(GetCallerRegsCount(target.GetArch(), false));
139 
140     frame->SetFpCalleesOffset(offset);
141     offset += helpers::ToSigned(GetCalleeRegsCount(target.GetArch(), true));
142     frame->SetCalleesOffset(offset);
143 
144     frame->SetSpillsCount(spillsCount);
145     frame->SetFrameSize(frameSize);
146 
147     SetFrameInfo(frame);
148 }
149 
EmitTailCallIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)150 void CodegenBoundary::EmitTailCallIntrinsic(IntrinsicInst *inst, [[maybe_unused]] Reg dst, [[maybe_unused]] SRCREGS src)
151 {
152     auto location = inst->GetLocation(0);
153     ASSERT(location.IsFixedRegister() && location.IsRegisterValid());
154     auto addr = Reg(location.GetValue(), GetTarget().GetPtrRegType());
155     RegMask liveoutMask = GetLiveOut(inst->GetBasicBlock());
156     ScopedTmpReg target(GetEncoder());
157     if (!liveoutMask.Test(addr.GetId())) {
158         ASSERT(target.GetReg().IsValid());
159         GetEncoder()->EncodeMov(target, addr);
160         addr = target.GetReg();
161     }
162     ASSERT(addr.IsValid());
163     RemoveBoundaryFrame(inst->GetBasicBlock());
164     GetEncoder()->EncodeJump(addr);
165 }
166 
RemoveBoundaryFrame(const BasicBlock * bb) const167 void CodegenBoundary::RemoveBoundaryFrame(const BasicBlock *bb) const
168 {
169     auto encoder = GetEncoder();
170     auto frame = GetFrameInfo();
171 
172     RegMask liveoutMask = GetLiveOut(bb);
173 
174     RegMask calleeRegs = GetCalleeRegsMask(GetArch(), false) & ~GetTarget().GetTempRegsMask().to_ulong();
175     calleeRegs &= ~liveoutMask;
176     calleeRegs.reset(ThreadReg().GetId());
177     RegMask calleeVregs = GetCalleeRegsMask(GetArch(), true) & ~GetTarget().GetTempVRegsMask().to_ulong();
178     RegMask callerRegs = GetCallerRegsMask(GetArch(), false) & ~GetTarget().GetTempRegsMask().to_ulong();
179     callerRegs &= ~liveoutMask;
180     RegMask callerVregs = GetCallerRegsMask(GetArch(), true) & ~GetTarget().GetTempVRegsMask().to_ulong();
181 
182     Reg base = GetTarget().GetFrameReg();
183     auto fl = GetFrameLayout();
184     ssize_t offset = fl.GetOffset<CFrameLayout::OffsetOrigin::FP, CFrameLayout::OffsetUnit::SLOTS>(
185         CFrameLayout::GetStackStartSlot() + fl.GetCallerLastSlot(false));
186     encoder->LoadRegisters(callerRegs, false, -offset, base, GetCallerRegsMask(GetArch(), false));
187     offset = fl.GetOffset<CFrameLayout::OffsetOrigin::FP, CFrameLayout::OffsetUnit::SLOTS>(
188         CFrameLayout::GetStackStartSlot() + fl.GetCallerLastSlot(true));
189     encoder->LoadRegisters(callerVregs, true, -offset, base, GetCallerRegsMask(GetArch(), true));
190 
191     base = frame->GetCalleesRelativeFp() ? GetTarget().GetFrameReg() : GetTarget().GetStackReg();
192     encoder->LoadRegisters(calleeRegs, false, frame->GetCalleesOffset(), base, GetCalleeRegsMask(GetArch(), false));
193     encoder->LoadRegisters(calleeVregs, true, frame->GetFpCalleesOffset(), base, GetCalleeRegsMask(GetArch(), true));
194 
195     encoder->EncodeAdd(GetTarget().GetStackReg(), GetTarget().GetStackReg(), Imm(frame->GetFrameSize()));
196 
197     if (GetTarget().SupportLinkReg()) {
198         static constexpr ssize_t FP_OFFSET = -3;
199         const auto wordSize = static_cast<ssize_t>(GetTarget().WordSize());
200         constexpr ssize_t MAX_ALLOWED = std::numeric_limits<ssize_t>::max() / -FP_OFFSET;
201         if (wordSize > MAX_ALLOWED) {
202             LOG(FATAL, IRTOC) << "Frame offset calculation overflow";
203             return;
204         }
205         encoder->EncodeLdr(GetTarget().GetLinkReg(), false, MemRef(GetTarget().GetStackReg(), -wordSize));
206         encoder->EncodeLdr(GetTarget().GetFrameReg(), false, MemRef(GetTarget().GetStackReg(), FP_OFFSET * wordSize));
207     }
208 }
209 }  // namespace ark::compiler
210