1/*
2 * Copyright (C) 2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "AirLowerStackArgs.h"
28
29#if ENABLE(B3_JIT)
30
31#include "AirArgInlines.h"
32#include "AirCode.h"
33#include "AirInsertionSet.h"
34#include "AirInstInlines.h"
35#include "AirPhaseScope.h"
36#include "StackAlignment.h"
37#include <wtf/ListDump.h>
38
39namespace JSC { namespace B3 { namespace Air {
40
41void lowerStackArgs(Code& code)
42{
43 PhaseScope phaseScope(code, "lowerStackArgs");
44
45 // Now we need to deduce how much argument area we need.
46 for (BasicBlock* block : code) {
47 for (Inst& inst : *block) {
48 for (Arg& arg : inst.args) {
49 if (arg.isCallArg()) {
50 // For now, we assume that we use 8 bytes of the call arg. But that's not
51 // such an awesome assumption.
52 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=150454
53 ASSERT(arg.offset() >= 0);
54 code.requestCallArgAreaSizeInBytes(arg.offset() + 8);
55 }
56 }
57 }
58 }
59
60 code.setFrameSize(code.frameSize() + code.callArgAreaSizeInBytes());
61
62 // Finally, transform the code to use Addr's instead of StackSlot's. This is a lossless
63 // transformation since we can search the StackSlots array to figure out which StackSlot any
64 // offset-from-FP refers to.
65
66 InsertionSet insertionSet(code);
67 for (BasicBlock* block : code) {
68 // FIXME We can keep track of the last large offset which was materialized in this block, and reuse the register
69 // if it hasn't been clobbered instead of renetating imm+add+addr every time. https://bugs.webkit.org/show_bug.cgi?id=171387
70
71 for (unsigned instIndex = 0; instIndex < block->size(); ++instIndex) {
72 Inst& inst = block->at(instIndex);
73
74 if (isARM64() && (inst.kind.opcode == Lea32 || inst.kind.opcode == Lea64)) {
75 // On ARM64, Lea is just an add. We can't handle this below because
76 // taking into account the Width to see if we can compute the immediate
77 // is wrong.
78 auto lowerArmLea = [&] (Value::OffsetType offset, Tmp base) {
79 ASSERT(inst.args[1].isTmp());
80
81 if (Arg::isValidImmForm(offset))
82 inst = Inst(inst.kind.opcode == Lea32 ? Add32 : Add64, inst.origin, Arg::imm(offset), base, inst.args[1]);
83 else {
84 ASSERT(pinnedExtendedOffsetAddrRegister());
85 Air::Tmp tmp = Air::Tmp(*pinnedExtendedOffsetAddrRegister());
86 Arg offsetArg = Arg::bigImm(offset);
87 insertionSet.insert(instIndex, Move, inst.origin, offsetArg, tmp);
88 inst = Inst(inst.kind.opcode == Lea32 ? Add32 : Add64, inst.origin, tmp, base, inst.args[1]);
89 }
90 };
91
92 switch (inst.args[0].kind()) {
93 case Arg::Stack: {
94 StackSlot* slot = inst.args[0].stackSlot();
95 lowerArmLea(inst.args[0].offset() + slot->offsetFromFP(), Tmp(GPRInfo::callFrameRegister));
96 break;
97 }
98 case Arg::CallArg:
99 lowerArmLea(inst.args[0].offset() - code.frameSize(), Tmp(GPRInfo::callFrameRegister));
100 break;
101 case Arg::Addr:
102 lowerArmLea(inst.args[0].offset(), inst.args[0].base());
103 break;
104 case Arg::ExtendedOffsetAddr:
105 ASSERT_NOT_REACHED();
106 break;
107 default:
108 break;
109 }
110
111 continue;
112 }
113
114 inst.forEachArg(
115 [&] (Arg& arg, Arg::Role role, Bank, Width width) {
116 auto stackAddr = [&] (Value::OffsetType offsetFromFP) -> Arg {
117 int32_t offsetFromSP = offsetFromFP + code.frameSize();
118
119 if (inst.admitsExtendedOffsetAddr(arg)) {
120 // Stackmaps and patchpoints expect addr inputs relative to SP or FP only. We might as well
121 // not even bother generating an addr with valid form for these opcodes since extended offset
122 // addr is always valid.
123 return Arg::extendedOffsetAddr(offsetFromFP);
124 }
125
126 Arg result = Arg::addr(Air::Tmp(GPRInfo::callFrameRegister), offsetFromFP);
127 if (result.isValidForm(width))
128 return result;
129
130 result = Arg::addr(Air::Tmp(MacroAssembler::stackPointerRegister), offsetFromSP);
131 if (result.isValidForm(width))
132 return result;
133#if CPU(ARM64)
134 ASSERT(pinnedExtendedOffsetAddrRegister());
135 Air::Tmp tmp = Air::Tmp(*pinnedExtendedOffsetAddrRegister());
136
137 Arg largeOffset = Arg::isValidImmForm(offsetFromSP) ? Arg::imm(offsetFromSP) : Arg::bigImm(offsetFromSP);
138 insertionSet.insert(instIndex, Move, inst.origin, largeOffset, tmp);
139 insertionSet.insert(instIndex, Add64, inst.origin, Air::Tmp(MacroAssembler::stackPointerRegister), tmp);
140 result = Arg::addr(tmp, 0);
141 return result;
142#elif CPU(X86_64)
143 // Can't happen on x86: immediates are always big enough for frame size.
144 RELEASE_ASSERT_NOT_REACHED();
145#else
146#error Unhandled architecture.
147#endif
148 };
149
150 switch (arg.kind()) {
151 case Arg::Stack: {
152 StackSlot* slot = arg.stackSlot();
153 if (Arg::isZDef(role)
154 && slot->kind() == StackSlotKind::Spill
155 && slot->byteSize() > bytes(width)) {
156 // Currently we only handle this simple case because it's the only one
157 // that arises: ZDef's are only 32-bit right now. So, when we hit these
158 // assertions it means that we need to implement those other kinds of
159 // zero fills.
160 RELEASE_ASSERT(slot->byteSize() == 8);
161 RELEASE_ASSERT(width == Width32);
162
163 RELEASE_ASSERT(isValidForm(StoreZero32, Arg::Stack));
164 insertionSet.insert(
165 instIndex + 1, StoreZero32, inst.origin,
166 stackAddr(arg.offset() + 4 + slot->offsetFromFP()));
167 }
168 arg = stackAddr(arg.offset() + slot->offsetFromFP());
169 break;
170 }
171 case Arg::CallArg:
172 arg = stackAddr(arg.offset() - code.frameSize());
173 break;
174 default:
175 break;
176 }
177 }
178 );
179 }
180 insertionSet.execute(block);
181 }
182}
183
184} } } // namespace JSC::B3::Air
185
186#endif // ENABLE(B3_JIT)
187
188