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