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