source: webkit/trunk/Source/JavaScriptCore/offlineasm/transform.rb

Last change on this file was 260323, checked in by [email protected], 5 years ago

Redesign how we do for-of iteration for JSArrays
https://bugs.webkit.org/show_bug.cgi?id=175454

JSTests:

Reviewed by Filip Pizlo.

  • microbenchmarks/for-of-iterate-array-entries.js:

(foo):

  • stress/custom-iterators.js:

(catch):
(iter.return):
(iteratorInterfaceErrorTest):
(iteratorInterfaceErrorTestReturn):
(iteratorInterfaceBreakTestReturn):

  • stress/for-of-array-different-globals.js: Added.

(foo):
(array.Symbol.iterator.proto.next):

  • stress/for-of-array-mixed-values.js: Added.

(test):

  • stress/for-of-no-direct-loop-back-edge-osr.js: Added.

(osrIfFinalTier):
(test):

  • stress/generator-containing-for-of-on-map.js: Added.

(find):
(i.let.v.of.find):

  • stress/generator-containing-for-of-on-set.js: Added.

(find):
(set add):

  • stress/osr-from-for-of-done-getter.js: Added.

(i.let.iterable.next.return.get done):
(i.let.iterable.next):
(i.let.iterable.Symbol.iterator):
(i.let.iterable.return):

  • stress/osr-from-for-of-value-getter.js: Added.

(i.let.iterable.next.return.get value):
(i.let.iterable.next):
(i.let.iterable.Symbol.iterator):
(i.let.iterable.return):

  • stress/throw-for-of-next-returns-non-object.js: Added.

(i.let.iterable.next):
(i.let.iterable.Symbol.iterator):
(i.let.iterable.return):
(i.catch):

  • stress/throw-from-done-getter-in-for-of-header.js: Added.

(i.let.iterable.next):
(i.let.iterable.get done):
(i.let.iterable.Symbol.iterator):
(i.let.iterable.return):
(i.catch):

  • stress/throw-from-next-in-for-of-header.js: Added.

(i.let.iterable.next):
(i.let.iterable.Symbol.iterator):
(i.let.iterable.return):
(i.catch):

  • stress/throw-from-value-getter-in-for-of-header.js: Added.

(i.let.iterable.next):
(i.let.iterable.get value):
(i.let.iterable.Symbol.iterator):
(i.let.iterable.return):
(i.catch):

  • stress/webidl-tokenizer-for-of.js: Added.

(tokenise.attemptTokenMatch):
(tokenise):
(Tokeniser):
(Tokeniser.prototype.probe):
(Tokeniser.prototype.consume):
(Tokeniser.prototype.unconsume):

Source/JavaScriptCore:

Reviewed by Filip Pizlo and Saam Barati.

This patch intrinsics for-of iteration for JSArrays when they are
being iterated with the built-in Symbol.iterator. We do this by
adding two new bytecodes op_iterator_open and
op_iterator_next. These bytecodes are essentially a fused set of
existing bytecodes with a special case for our intrinsiced JSArray
case. This patch only adds support for these instructions on
64-bit.

The op_iterator_open bytecode is semantically the same as:
iterator = symbolIterator.@call(iterable);
next = iterator.next;

where iterable is the rhs of the for-of and symbolIterator is the
result of running iterable.symbolIterator;

The op_iterator_next bytecode is semantically the same as:
nextResult = next.@call(iterator);
done = nextResult.done;
value = done ? (undefined / bottom) : nextResult.value;

where nextResult is a temporary (the value VirtualRegister in the
LLInt/Baseline and a tmp in the DFG).

In order to make sure these bytecodes have the same perfomance as
the existing bytecode sequence, we need to make sure we have the
same profiling data and inline caching. Most of the existing
get_by_id code assumed a particular bytecode member name was the
same in each flavor get_by_id access. This patch adds template
specialized functions that vend the correct
Profile/VirtualRegister for the current bytecode/checkpoint. This
means we can have meaningful names for our Bytecode structs and
still use the generic functions.

In the LLInt most of the logic for calls/get_by_id had to be
factored into helper macros, so we could have bytecodes that are
some combination of those.

The trickiest part of this patch was getting the hand rolled DFG
IR to work correctly. This is because we don't have a great way to
express large chucks of DFG graph that doesn't involve manually
tracking all the DFG's invariants. Such as:

1) Flushing/Phantoming values at the end of each block.
2) Rolling forwards and backwards the BytecodeIndex when switching

blocks.

3) Remembering to GetLocal each variable at the top of every block.
4) Ensuring that the JSValue stored to the op_iterator_next.m_value

local does not cause us to OSR exit at the set local.

(4) is handled by a new function, bottomValueMatchingSpeculation,
on DFGGraph that produces a FrozenValue that is roughly the bottom
for a given speculated type. In a future patch we should make this
more complete, probably by adding a VM::bottomCellForSetLocal that
prediction propagation and AI know how treat as a true bottom
value. See: https://bugs.webkit.org/show_bug.cgi?id=210694

Lastly, this patch changes the DFG NodeType, CheckCell to be
CheckIsConstant. CheckIsConstant is equivalent to the == operator
on JSValue where it just checks the register values are the
same. In order to keep the same perf that we had for CheckCell,
CheckIsConstant supports CellUse.

  • CMakeLists.txt:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • assembler/MacroAssemblerARM64.h:

(JSC::MacroAssemblerARM64::or8):
(JSC::MacroAssemblerARM64::store8):

  • assembler/MacroAssemblerX86_64.h:

(JSC::MacroAssemblerX86_64::or8):

  • bytecode/ArrayProfile.h:

(JSC::ArrayProfile::observeStructureID):
(JSC::ArrayProfile::observeStructure):

  • bytecode/BytecodeList.rb:
  • bytecode/BytecodeLivenessAnalysis.cpp:

(JSC::tmpLivenessForCheckpoint):

  • bytecode/BytecodeOperandsForCheckpoint.h: Added.

(JSC::arrayProfileForImpl):
(JSC::hasArrayProfileFor):
(JSC::arrayProfileFor):
(JSC::valueProfileForImpl):
(JSC::hasValueProfileFor):
(JSC::valueProfileFor):
(JSC::destinationFor):
(JSC::calleeFor):
(JSC::argumentCountIncludingThisFor):
(JSC::stackOffsetInRegistersForCall):
(JSC::callLinkInfoFor):

  • bytecode/BytecodeUseDef.cpp:

(JSC::computeUsesForBytecodeIndexImpl):
(JSC::computeDefsForBytecodeIndexImpl):

  • bytecode/CallLinkInfo.cpp:

(JSC::CallLinkInfo::callTypeFor):

  • bytecode/CallLinkStatus.cpp:

(JSC::CallLinkStatus::computeFromLLInt):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::finishCreation):
(JSC::CodeBlock::finalizeLLIntInlineCaches):
(JSC::CodeBlock::tryGetValueProfileForBytecodeIndex):

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::instructionAt const):

  • bytecode/CodeBlockInlines.h:

(JSC::CodeBlock::forEachValueProfile):
(JSC::CodeBlock::forEachArrayProfile):

  • bytecode/GetByStatus.cpp:

(JSC::GetByStatus::computeFromLLInt):

  • bytecode/Instruction.h:

(JSC::BaseInstruction::width const):
(JSC::BaseInstruction::hasCheckpoints const):
(JSC::BaseInstruction::asKnownWidth const):
(JSC::BaseInstruction::wide16 const):
(JSC::BaseInstruction::wide32 const):

  • bytecode/InstructionStream.h:
  • bytecode/IterationModeMetadata.h: Copied from Source/JavaScriptCore/bytecode/SuperSampler.h.
  • bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp:

(JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::fireInternal):
(JSC::LLIntPrototypeLoadAdaptiveStructureWatchpoint::clearLLIntGetByIdCache):

  • bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.h:
  • bytecode/Opcode.h:
  • bytecode/SpeculatedType.h:

(JSC::isSubtypeSpeculation):
(JSC::speculationContains):

  • bytecode/SuperSampler.h:

(JSC::SuperSamplerScope::release):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitGenericEnumeration):
(JSC::BytecodeGenerator::emitEnumeration):
(JSC::BytecodeGenerator::emitIsEmpty):
(JSC::BytecodeGenerator::emitIteratorOpen):
(JSC::BytecodeGenerator::emitIteratorNext):
(JSC::BytecodeGenerator::emitGetGenericIterator):
(JSC::BytecodeGenerator::emitIteratorGenericNext):
(JSC::BytecodeGenerator::emitIteratorGenericNextWithValue):
(JSC::BytecodeGenerator::emitIteratorGenericClose):
(JSC::BytecodeGenerator::emitGetAsyncIterator):
(JSC::BytecodeGenerator::emitDelegateYield):
(JSC::BytecodeGenerator::emitIteratorNextWithValue): Deleted.
(JSC::BytecodeGenerator::emitIteratorClose): Deleted.
(JSC::BytecodeGenerator::emitGetIterator): Deleted.

  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::ArrayPatternNode::bindValue const):

  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
(JSC::DFG::AbstractInterpreter<AbstractStateType>::forAllValues):

  • dfg/DFGAtTailAbstractState.h:

(JSC::DFG::AtTailAbstractState::size const):
(JSC::DFG::AtTailAbstractState::numberOfTmps const):
(JSC::DFG::AtTailAbstractState::atIndex):
(JSC::DFG::AtTailAbstractState::tmp):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::progressToNextCheckpoint):
(JSC::DFG::ByteCodeParser::get):
(JSC::DFG::ByteCodeParser::set):
(JSC::DFG::ByteCodeParser::jsConstant):
(JSC::DFG::ByteCodeParser::weakJSConstant):
(JSC::DFG::ByteCodeParser::addCall):
(JSC::DFG::ByteCodeParser::allocateUntargetableBlock):
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::emitFunctionChecks):
(JSC::DFG::ByteCodeParser::inlineCall):
(JSC::DFG::ByteCodeParser::handleCallVariant):
(JSC::DFG::ByteCodeParser::handleVarargsInlining):
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::handleMinMax):
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
(JSC::DFG::ByteCodeParser::handleDOMJITCall):
(JSC::DFG::ByteCodeParser::handleIntrinsicGetter):
(JSC::DFG::ByteCodeParser::handleDOMJITGetter):
(JSC::DFG::ByteCodeParser::handleModuleNamespaceLoad):
(JSC::DFG::ByteCodeParser::handleTypedArrayConstructor):
(JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry):
(JSC::DFG::ByteCodeParser::handlePutByVal):
(JSC::DFG::ByteCodeParser::handleCreateInternalFieldObject):
(JSC::DFG::ByteCodeParser::parse):

  • dfg/DFGCFGSimplificationPhase.cpp:

(JSC::DFG::CFGSimplificationPhase::keepOperandAlive):
(JSC::DFG::CFGSimplificationPhase::jettisonBlock):
(JSC::DFG::CFGSimplificationPhase::mergeBlocks):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGConstantFoldingPhase.cpp:

(JSC::DFG::ConstantFoldingPhase::foldConstants):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::addStringReplacePrimordialChecks):

  • dfg/DFGForAllKills.h:

(JSC::DFG::forAllKilledOperands):

  • dfg/DFGGraph.cpp:

(JSC::DFG::Graph::bottomValueMatchingSpeculation):

  • dfg/DFGGraph.h:
  • dfg/DFGInPlaceAbstractState.cpp:

(JSC::DFG::InPlaceAbstractState::beginBasicBlock):
(JSC::DFG::InPlaceAbstractState::initialize):
(JSC::DFG::InPlaceAbstractState::endBasicBlock):
(JSC::DFG::InPlaceAbstractState::merge):

  • dfg/DFGInPlaceAbstractState.h:

(JSC::DFG::InPlaceAbstractState::size const):
(JSC::DFG::InPlaceAbstractState::numberOfTmps const):
(JSC::DFG::InPlaceAbstractState::atIndex):
(JSC::DFG::InPlaceAbstractState::operand):
(JSC::DFG::InPlaceAbstractState::local):
(JSC::DFG::InPlaceAbstractState::argument):
(JSC::DFG::InPlaceAbstractState::variableAt): Deleted.

  • dfg/DFGLazyJSValue.h:

(JSC::DFG::LazyJSValue::speculatedType const):

  • dfg/DFGNode.h:

(JSC::DFG::Node::hasConstant):
(JSC::DFG::Node::hasCellOperand):

  • dfg/DFGNodeType.h:
  • dfg/DFGOSRExitCompilerCommon.cpp:

(JSC::DFG::callerReturnPC):

  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileCheckIsConstant):
(JSC::DFG::SpeculativeJIT::compileCheckCell): Deleted.

  • dfg/DFGSpeculativeJIT.h:
  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGValidate.cpp:
  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCheckIsConstant):
(JSC::FTL::DFG::LowerDFGToB3::compileCheckCell): Deleted.

  • generator/DSL.rb:
  • generator/Metadata.rb:
  • generator/Section.rb:
  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):

  • jit/JIT.h:
  • jit/JITCall.cpp:

(JSC::JIT::emitPutCallResult):
(JSC::JIT::compileSetupFrame):
(JSC::JIT::compileOpCall):
(JSC::JIT::emit_op_iterator_open):
(JSC::JIT::emitSlow_op_iterator_open):
(JSC::JIT::emit_op_iterator_next):
(JSC::JIT::emitSlow_op_iterator_next):

  • jit/JITCall32_64.cpp:

(JSC::JIT::emit_op_iterator_open):
(JSC::JIT::emitSlow_op_iterator_open):
(JSC::JIT::emit_op_iterator_next):
(JSC::JIT::emitSlow_op_iterator_next):

  • jit/JITInlines.h:

(JSC::JIT::updateTopCallFrame):
(JSC::JIT::advanceToNextCheckpoint):
(JSC::JIT::emitJumpSlowToHotForCheckpoint):
(JSC::JIT::emitValueProfilingSite):

  • jit/JITOperations.cpp:
  • jit/JITOperations.h:
  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::setupGetByIdPrototypeCache):
(JSC::LLInt::performLLIntGetByID):
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
(JSC::LLInt::genericCall):
(JSC::LLInt::handleIteratorOpenCheckpoint):
(JSC::LLInt::handleIteratorNextCheckpoint):
(JSC::LLInt::slow_path_checkpoint_osr_exit):
(JSC::LLInt::llint_dump_value):

  • llint/LowLevelInterpreter.asm:
  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
  • offlineasm/transform.rb:
  • runtime/CommonSlowPaths.cpp:

(JSC::iterator_open_try_fast):
(JSC::iterator_open_try_fast_narrow):
(JSC::iterator_open_try_fast_wide16):
(JSC::iterator_open_try_fast_wide32):
(JSC::iterator_next_try_fast):
(JSC::iterator_next_try_fast_narrow):
(JSC::iterator_next_try_fast_wide16):
(JSC::iterator_next_try_fast_wide32):

  • runtime/CommonSlowPaths.h:
  • runtime/Intrinsic.cpp:

(JSC::interationKindForIntrinsic):

  • runtime/Intrinsic.h:
  • runtime/JSArrayIterator.h:
  • runtime/JSCJSValue.h:
  • runtime/JSCJSValueInlines.h:

(JSC::JSValue::isCallable const):

  • runtime/JSCast.h:
  • runtime/JSGlobalObject.h:

(JSC::JSGlobalObject::arrayProtoValuesFunctionConcurrently const):

  • runtime/OptionsList.h:
  • runtime/Structure.cpp:

(JSC::Structure::dumpBrief const):

Source/WTF:

Reviewed by Filip Pizlo.

  • wtf/EnumClassOperatorOverloads.h:
File size: 18.7 KB
Line 
1# Copyright (C) 2011 Apple Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6# 1. Redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer.
8# 2. Redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution.
11#
12# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
13# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
16# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
22# THE POSSIBILITY OF SUCH DAMAGE.
23
24require "config"
25require "ast"
26
27#
28# node.resolveSettings(settings)
29#
30# Construct a new AST that does not have any IfThenElse nodes by
31# substituting concrete boolean values for each Setting.
32#
33
34class Node
35 def resolveSettings(settings)
36 mapChildren {
37 | child |
38 child.resolveSettings(settings)
39 }
40 end
41end
42
43class True
44 def resolveSettings(settings)
45 self
46 end
47end
48
49class False
50 def resolveSettings(settings)
51 self
52 end
53end
54
55class Setting
56 def resolveSettings(settings)
57 settings[@name].asNode
58 end
59end
60
61class And
62 def resolveSettings(settings)
63 (@left.resolveSettings(settings).value and @right.resolveSettings(settings).value).asNode
64 end
65end
66
67class Or
68 def resolveSettings(settings)
69 (@left.resolveSettings(settings).value or @right.resolveSettings(settings).value).asNode
70 end
71end
72
73class Not
74 def resolveSettings(settings)
75 (not @child.resolveSettings(settings).value).asNode
76 end
77end
78
79class IfThenElse
80 def resolveSettings(settings)
81 if @predicate.resolveSettings(settings).value
82 @thenCase.resolveSettings(settings)
83 else
84 @elseCase.resolveSettings(settings)
85 end
86 end
87end
88
89class Sequence
90 def resolveSettings(settings)
91 newList = []
92 @list.each {
93 | item |
94 item = item.resolveSettings(settings)
95 if item.is_a? Sequence
96 newList += item.list
97 else
98 newList << item
99 end
100 }
101 Sequence.new(codeOrigin, newList)
102 end
103end
104
105#
106# node.demacroify(macros)
107# node.substitute(mapping)
108#
109# demacroify() constructs a new AST that does not have any Macro
110# nodes, while substitute() replaces Variable nodes with the given
111# nodes in the mapping.
112#
113
114class Node
115 def demacroify(macros)
116 mapChildren {
117 | child |
118 child.demacroify(macros)
119 }
120 end
121
122 def freshVariables(mapping)
123 mapChildren {
124 | child |
125 child.freshVariables(mapping)
126 }
127 end
128
129 def substitute(mapping)
130 mapChildren {
131 | child |
132 child.substitute(mapping)
133 }
134 end
135
136 def substituteLabels(mapping)
137 mapChildren {
138 | child |
139 child.substituteLabels(mapping)
140 }
141 end
142end
143
144$uniqueMacroVarID = 0
145class Macro
146 def freshVariables(mapping = {})
147 myMapping = mapping.dup
148 newVars = []
149 variables.each do |var|
150 $uniqueMacroVarID += 1
151 newVar = Variable.forName(var.codeOrigin, "_var#{$uniqueMacroVarID}", var.originalName)
152 newVars << newVar
153 myMapping[var] = newVar
154 end
155 Macro.new(codeOrigin, name, newVars, body.freshVariables(myMapping))
156 end
157
158 def substitute(mapping)
159 myMapping = {}
160 mapping.each_pair {
161 | key, value |
162 unless @variables.include? key
163 myMapping[key] = value
164 end
165 }
166 mapChildren {
167 | child |
168 child.substitute(myMapping)
169 }
170 end
171end
172
173class MacroCall
174 def freshVariables(mapping)
175 newName = Variable.forName(codeOrigin, name, originalName)
176 if mapping[newName]
177 newName = mapping[newName]
178 end
179 newOperands = operands.map { |operand| operand.freshVariables(mapping) }
180 MacroCall.new(codeOrigin, newName.name, newOperands, annotation, originalName)
181 end
182end
183
184$concatenation = /%([a-zA-Z0-9_]+)%/
185class Variable
186 def freshVariables(mapping)
187 if @name =~ $concatenation
188 name = @name.gsub($concatenation) { |match|
189 var = Variable.forName(codeOrigin, match[1...-1])
190 if mapping[var]
191 "%#{mapping[var].name}%"
192 else
193 match
194 end
195 }
196 Variable.forName(codeOrigin, name)
197 elsif mapping[self]
198 mapping[self]
199 else
200 self
201 end
202 end
203
204 def substitute(mapping)
205 if @name =~ $concatenation
206 name = @name.gsub($concatenation) { |match|
207 var = Variable.forName(codeOrigin, match[1...-1])
208 if mapping[var]
209 mapping[var].name
210 else
211 match
212 end
213 }
214 Variable.forName(codeOrigin, name)
215 elsif mapping[self]
216 mapping[self]
217 else
218 self
219 end
220 end
221end
222
223class StructOffset
224 def freshVariables(mapping)
225 if dump =~ $concatenation
226 names = dump.gsub($concatenation) { |match|
227 var = Variable.forName(codeOrigin, match[1...-1])
228 if mapping[var]
229 "%#{mapping[var].name}%"
230 else
231 match
232 end
233 }.split('::')
234 StructOffset.forField(codeOrigin, names[0..-2].join('::'), names[-1])
235 else
236 self
237 end
238 end
239
240 def substitute(mapping)
241 if dump =~ $concatenation
242 names = dump.gsub($concatenation) { |match|
243 var = Variable.forName(codeOrigin, match[1...-1])
244 if mapping[var]
245 mapping[var].name
246 else
247 match
248 end
249 }.split('::')
250 StructOffset.forField(codeOrigin, names[0..-2].join('::'), names[-1])
251 else
252 self
253 end
254 end
255end
256
257class Label
258 def freshVariables(mapping)
259 if @name =~ $concatenation
260 name = @name.gsub($concatenation) { |match|
261 var = Variable.forName(codeOrigin, match[1...-1])
262 if mapping[var]
263 "%#{mapping[var].name}%"
264 else
265 match
266 end
267 }
268 result = Label.forName(codeOrigin, name, @definedInFile)
269 result.setGlobal() if global?
270 result.clearExtern unless extern?
271 result
272 else
273 self
274 end
275 end
276
277 def substitute(mapping)
278 if @name =~ $concatenation
279 name = @name.gsub($concatenation) { |match|
280 var = Variable.forName(codeOrigin, match[1...-1])
281 if mapping[var]
282 mapping[var].name
283 else
284 match
285 end
286 }
287 result = Label.forName(codeOrigin, name, @definedInFile)
288 result.setGlobal() if global?
289 result.clearExtern unless extern?
290 result
291 else
292 self
293 end
294 end
295end
296
297class ConstExpr
298 def freshVariables(mapping)
299 if @value =~ $concatenation
300 value = @value.gsub($concatenation) { |match|
301 var = Variable.forName(codeOrigin, match[1...-1])
302 if mapping[var]
303 "%#{mapping[var].name}%"
304 else
305 match
306 end
307 }
308 ConstExpr.forName(codeOrigin, value)
309 else
310 self
311 end
312 end
313
314 def substitute(mapping)
315 if @value =~ $concatenation
316 value = @value.gsub($concatenation) { |match|
317 var = Variable.forName(codeOrigin, match[1...-1])
318 if mapping[var]
319 mapping[var].name
320 else
321 match
322 end
323 }
324 ConstExpr.forName(codeOrigin, value)
325 else
326 self
327 end
328 end
329end
330
331class Sizeof
332 def freshVariables(mapping)
333 if struct =~ $concatenation
334 value = struct.gsub($concatenation) { |match|
335 var = Variable.forName(codeOrigin, match[1...-1])
336 if mapping[var]
337 "%#{mapping[var].name}%"
338 else
339 match
340 end
341 }
342 Sizeof.forName(codeOrigin, value)
343 else
344 self
345 end
346 end
347
348 def substitute(mapping)
349 if struct =~ $concatenation
350 value = struct.gsub($concatenation) { |match|
351 var = Variable.forName(codeOrigin, match[1...-1])
352 if mapping[var]
353 mapping[var].name
354 else
355 match
356 end
357 }
358 Sizeof.forName(codeOrigin, value)
359 else
360 self
361 end
362 end
363end
364
365class LocalLabel
366 def substituteLabels(mapping)
367 if mapping[self]
368 mapping[self]
369 else
370 self
371 end
372 end
373end
374
375class MacroError < StandardError
376end
377
378class Sequence
379 def substitute(constants)
380 newList = []
381 myConstants = constants.dup
382 @list.each {
383 | item |
384 if item.is_a? ConstDecl
385 myConstants[item.variable] = item.value.substitute(myConstants)
386 else
387 newList << item.substitute(myConstants)
388 end
389 }
390 Sequence.new(codeOrigin, newList)
391 end
392
393 def renameLabels(comment)
394 mapping = {}
395
396 @list.each {
397 | item |
398 if item.is_a? LocalLabel
399 mapping[item] = LocalLabel.unique(if comment then comment + "_" else "" end + item.cleanName)
400 end
401 }
402
403 substituteLabels(mapping)
404 end
405
406 @@demacroifyStack = []
407 def macroError(msg)
408 backtrace = @@demacroifyStack.reverse.map { |macroCall|
409 "#{macroCall.codeOrigin} in call to #{macroCall.originalName}"
410 }
411 raise MacroError, msg, backtrace
412 end
413
414 def demacroify(macros)
415 myMacros = macros.dup
416 # We do an initial pass looking for all macros in order to allow forward references
417 @list.each {
418 | item |
419 if item.is_a? Macro
420 myMacros[item.name] = item.freshVariables
421 end
422 }
423 newList = []
424 @list.each {
425 | item |
426 if item.is_a? Macro
427 # Ignore. We already looked for macros above and they should not be part of the final output
428 elsif item.is_a? MacroCall
429 @@demacroifyStack << item
430 mapping = {}
431 myMyMacros = myMacros.dup
432 macro = myMacros[item.name]
433 macroError "Could not find macro #{item.originalName}" unless macro
434 macroError "Argument count mismatch for call to #{item.originalName} (expected #{macro.variables.size} but got #{item.operands.size} arguments for macro #{item.originalName} defined at #{macro.codeOrigin})" unless item.operands.size == macro.variables.size
435 item.operands.size.times {
436 | idx |
437 if item.operands[idx].is_a? Variable and myMacros[item.operands[idx].name]
438 myMyMacros[macro.variables[idx].name] = myMacros[item.operands[idx].name]
439 mapping[macro.variables[idx]] = nil
440 elsif item.operands[idx].is_a? Macro
441 myMyMacros[macro.variables[idx].name] = item.operands[idx].freshVariables
442 mapping[macro.variables[idx]] = nil
443 else
444 myMyMacros[macro.variables[idx]] = nil
445 mapping[macro.variables[idx]] = item.operands[idx]
446 end
447 }
448 if item.annotation
449 newList << Instruction.new(item.codeOrigin, "localAnnotation", [], item.annotation)
450 end
451 newList += macro.body.substitute(mapping).demacroify(myMyMacros).renameLabels(item.originalName).list
452
453 @@demacroifyStack.pop
454 else
455 newList << item.demacroify(myMacros)
456 end
457 }
458 Sequence.new(codeOrigin, newList).substitute({})
459 end
460end
461
462#
463# node.resolveOffsets(offsets, sizes)
464#
465# Construct a new AST that has offset values instead of symbolic
466# offsets.
467#
468
469class Node
470 def resolveOffsets(constantsMap)
471 mapChildren {
472 | child |
473 child.resolveOffsets(constantsMap)
474 }
475 end
476end
477
478class StructOffset
479 def resolveOffsets(constantsMap)
480 if constantsMap[self]
481 Immediate.new(codeOrigin, constantsMap[self])
482 else
483 puts "Could not find #{self.inspect} in #{constantsMap.keys.inspect}"
484 puts "sizes = #{constantsMap.inspect}"
485 raise
486 end
487 end
488end
489
490class Sizeof
491 def resolveOffsets(constantsMap)
492 if constantsMap[self]
493 Immediate.new(codeOrigin, constantsMap[self])
494 else
495 puts "Could not find #{self.inspect} in #{constantsMap.keys.inspect}"
496 puts "sizes = #{constantsMap.inspect}"
497 raise
498 end
499 end
500end
501
502class ConstExpr
503 def resolveOffsets(constantsMap)
504 if constantsMap[self]
505 Immediate.new(codeOrigin, constantsMap[self])
506 else
507 puts "Could not find #{self.inspect} in #{constantsMap.keys.inspect}"
508 puts "sizes = #{constantsMap.inspect}"
509 raise
510 end
511 end
512end
513
514#
515# node.fold
516#
517# Resolve constant references and compute arithmetic expressions.
518#
519
520class Node
521 def fold
522 mapChildren {
523 | child |
524 child.fold
525 }
526 end
527end
528
529class AddImmediates
530 def fold
531 @left = @left.fold
532 @right = @right.fold
533
534 return right.plusOffset(@left.value) if @left.is_a? Immediate and @right.is_a? LabelReference
535 return left.plusOffset(@right.value) if @left.is_a? LabelReference and @right.is_a? Immediate
536
537 return self unless @left.is_a? Immediate
538 return self unless @right.is_a? Immediate
539 Immediate.new(codeOrigin, @left.value + @right.value)
540 end
541end
542
543class SubImmediates
544 def fold
545 @left = @left.fold
546 @right = @right.fold
547
548 return left.plusOffset(-@right.value) if @left.is_a? LabelReference and @right.is_a? Immediate
549
550 return self unless @left.is_a? Immediate
551 return self unless @right.is_a? Immediate
552 Immediate.new(codeOrigin, @left.value - @right.value)
553 end
554end
555
556class MulImmediates
557 def fold
558 @left = @left.fold
559 @right = @right.fold
560 return self unless @left.is_a? Immediate
561 return self unless @right.is_a? Immediate
562 Immediate.new(codeOrigin, @left.value * @right.value)
563 end
564end
565
566class NegImmediate
567 def fold
568 @child = @child.fold
569 return self unless @child.is_a? Immediate
570 Immediate.new(codeOrigin, -@child.value)
571 end
572end
573
574class OrImmediates
575 def fold
576 @left = @left.fold
577 @right = @right.fold
578 return self unless @left.is_a? Immediate
579 return self unless @right.is_a? Immediate
580 Immediate.new(codeOrigin, @left.value | @right.value)
581 end
582end
583
584class AndImmediates
585 def fold
586 @left = @left.fold
587 @right = @right.fold
588 return self unless @left.is_a? Immediate
589 return self unless @right.is_a? Immediate
590 Immediate.new(codeOrigin, @left.value & @right.value)
591 end
592end
593
594class XorImmediates
595 def fold
596 @left = @left.fold
597 @right = @right.fold
598 return self unless @left.is_a? Immediate
599 return self unless @right.is_a? Immediate
600 Immediate.new(codeOrigin, @left.value ^ @right.value)
601 end
602end
603
604class BitnotImmediate
605 def fold
606 @child = @child.fold
607 return self unless @child.is_a? Immediate
608 Immediate.new(codeOrigin, ~@child.value)
609 end
610end
611
612#
613# node.resolveAfterSettings(offsets, sizes)
614#
615# Compile assembly against a set of offsets.
616#
617
618class Node
619 def resolve(constantsMap)
620 demacroify({}).resolveOffsets(constantsMap).fold
621 end
622end
623
624#
625# node.validate
626#
627# Checks that the node is ready for backend compilation.
628#
629
630class Node
631 def validate
632 raise "Unresolved '#{dump}' at #{codeOriginString}"
633 end
634
635 def validateChildren
636 children.each {
637 | node |
638 node.validate
639 }
640 end
641end
642
643class Sequence
644 def validate
645 validateChildren
646
647 # Further verify that this list contains only instructions, labels, and skips.
648 @list.each {
649 | node |
650 unless node.is_a? Instruction or
651 node.is_a? Label or
652 node.is_a? LocalLabel or
653 node.is_a? Skip
654 raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
655 end
656 }
657 end
658end
659
660class Immediate
661 def validate
662 end
663end
664
665class StringLiteral
666 def validate
667 end
668end
669
670class RegisterID
671 def validate
672 end
673end
674
675class FPRegisterID
676 def validate
677 end
678end
679
680class Address
681 def validate
682 validateChildren
683 end
684end
685
686class BaseIndex
687 def validate
688 validateChildren
689 end
690end
691
692class AbsoluteAddress
693 def validate
694 validateChildren
695 end
696end
697
698class Instruction
699 def validate
700 validateChildren
701 end
702end
703
704class SubImmediates
705 def validate
706 raise "Invalid operand #{left.dump} to immediate subtraction" unless left.immediateOperand?
707 raise "Invalid operand #{right.dump} to immediate subtraction" unless right.immediateOperand?
708 end
709end
710
711class Error
712 def validate
713 end
714end
715
716class Label
717 def validate
718 raise "Unresolved substitution in Label #{name} at #{codeOrigin}" if name =~ /%/
719 end
720end
721
722class LocalLabel
723 def validate
724 end
725end
726
727class LabelReference
728 def validate
729 end
730end
731
732class LocalLabelReference
733 def validate
734 end
735end
736
737class Skip
738 def validate
739 end
740end
Note: See TracBrowser for help on using the repository browser.