@@ -12,6 +12,13 @@ class ISeqAllocator {
1212 return buffer
1313 }
1414
15+ func allocateConstants( _ slots: [ UntypedValue ] ) -> UnsafeBufferPointer < UntypedValue > {
16+ let buffer = UnsafeMutableBufferPointer< UntypedValue> . allocate( capacity: slots. count)
17+ _ = buffer. initialize ( fromContentsOf: slots)
18+ self . buffers. append ( UnsafeMutableRawBufferPointer ( buffer) )
19+ return UnsafeBufferPointer ( buffer)
20+ }
21+
1522 func allocateInstructions( capacity: Int ) -> UnsafeMutableBufferPointer < UInt64 > {
1623 assert ( _isPOD ( Instruction . self) , " Instruction must be POD " )
1724 let buffer = UnsafeMutableBufferPointer< UInt64> . allocate( capacity: capacity)
@@ -167,12 +174,16 @@ fileprivate struct MetaProgramCounter {
167174/// | SP+1 | Local variable 1 |
168175/// | ... | ... |
169176/// | SP+len(locals)-1 | Local variable N |
170- /// | SP+len(locals) | Value stack 0 |
171- /// | SP+len(locals)+1 | Value stack 1 |
177+ /// | SP+len(locals) | Const 0 |
178+ /// | SP+len(locals)+1 | Const 1 |
179+ /// | ... | ... |
180+ /// | SP+len(locals)+C | Const C |
181+ /// | SP+len(locals)+C | Value stack 0 |
182+ /// | SP+len(locals)+C+1 | Value stack 1 |
172183/// | ... | ... |
173- /// | SP+len(locals)+heighest(stack)-1 | Value stack N |
184+ /// | SP+len(locals)+C+ heighest(stack)-1 | Value stack N |
174185/// ```
175- ///
186+ /// where `C` is the number of constant slots.
176187
177188struct FrameHeaderLayout {
178189 let type : FunctionType
@@ -223,6 +234,10 @@ struct StackLayout {
223234 }
224235 }
225236
237+ func constReg( _ index: Int ) -> VReg {
238+ return VReg ( numberOfLocals + index)
239+ }
240+
226241 func dump< Target: TextOutputStream > ( to target: inout Target , iseq: InstructionSequence ) {
227242 let frameHeaderSize = FrameHeaderLayout . size ( of: frameHeader. type)
228243 let slotMinIndex = VReg ( - frameHeaderSize)
@@ -258,6 +273,9 @@ struct StackLayout {
258273 for i in 0 ..< numberOfLocals {
259274 writeSlot ( & target, VReg ( i) , " Local \( i) " )
260275 }
276+ for i in 0 ..< iseq. constants. count {
277+ writeSlot ( & target, VReg ( numberOfLocals + i) , " Const \( i) = \( iseq. constants [ i] ) " )
278+ }
261279 }
262280}
263281
@@ -346,17 +364,20 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
346364 enum MetaValueOnStack {
347365 case local( ValueType , LocalIndex )
348366 case stack( MetaValue )
367+ case const( ValueType , Int )
349368
350369 var type : MetaValue {
351370 switch self {
352371 case . local( let type, _) : return . some( type)
353372 case . stack( let type) : return type
373+ case . const( let type, _) : return . some( type)
354374 }
355375 }
356376 }
357377
358378 enum ValueSource {
359379 case vreg( VReg )
380+ case const( Int , ValueType )
360381 case local( LocalIndex )
361382 }
362383
@@ -366,9 +387,11 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
366387 private( set) var maxHeight : Int = 0
367388 var height : Int { values. count }
368389 let stackRegBase : VReg
390+ let stackLayout : StackLayout
369391
370- init ( stackRegBase: VReg ) {
371- self . stackRegBase = stackRegBase
392+ init ( stackLayout: StackLayout ) {
393+ self . stackRegBase = stackLayout. stackRegBase
394+ self . stackLayout = stackLayout
372395 }
373396
374397 mutating func push( _ value: ValueType ) -> VReg {
@@ -386,6 +409,10 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
386409 let type = try locals. type ( of: localIndex)
387410 self . values. append ( . local( type, localIndex) )
388411 }
412+ mutating func pushConst( _ index: Int , type: ValueType ) {
413+ assert ( index < stackLayout. constantSlotSize)
414+ self . values. append ( . const( type, index) )
415+ }
389416 mutating func preserveLocalsOnStack( _ localIndex: LocalIndex ) -> [ VReg ] {
390417 var copyTo : [ VReg ] = [ ]
391418 for i in 0 ..< values. count {
@@ -408,6 +435,18 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
408435 return copies
409436 }
410437
438+ mutating func preserveConstsOnStack( depth: Int ) -> [ ( source: VReg , to: VReg ) ] {
439+ var copies : [ ( source: VReg , to: VReg ) ] = [ ]
440+ for offset in 0 ..< depth {
441+ let valueIndex = self . values. count - 1 - offset
442+ let value = self . values [ valueIndex]
443+ guard case . const( let type, let index) = value else { continue }
444+ self . values [ valueIndex] = . stack( . some( type) )
445+ copies. append ( ( stackLayout. constReg ( index) , self . stackRegBase + VReg( valueIndex) ) )
446+ }
447+ return copies
448+ }
449+
411450 func peek( depth: Int ) -> ValueSource {
412451 return makeValueSource ( self . values [ height - 1 - depth] )
413452 }
@@ -419,6 +458,8 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
419458 source = . local( localIndex)
420459 case . stack:
421460 source = . vreg( stackRegBase + VReg( height) )
461+ case . const( let type, let index) :
462+ source = . const( index, type)
422463 }
423464 return source
424465 }
@@ -703,6 +744,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
703744 let functionIndex : FunctionIndex
704745 /// Whether a call to this function should be intercepted
705746 let intercepting : Bool
747+ var constantSlots : [ UntypedValue ]
706748
707749 init (
708750 allocator: ISeqAllocator ,
@@ -720,11 +762,12 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
720762 self . module = module
721763 self . iseqBuilder = ISeqBuilder ( runtimeConfiguration: runtimeConfiguration)
722764 self . controlStack = ControlStack ( )
723- self . valueStack = ValueStack ( stackRegBase: VReg ( locals. count) )
724- self . locals = Locals ( types: type. parameters + locals)
725765 self . stackLayout = StackLayout ( type: type, numberOfLocals: locals. count)
766+ self . valueStack = ValueStack ( stackLayout: stackLayout)
767+ self . locals = Locals ( types: type. parameters + locals)
726768 self . functionIndex = functionIndex
727769 self . intercepting = intercepting
770+ self . constantSlots = [ ]
728771
729772 do {
730773 let endLabel = self . iseqBuilder. allocLabel ( )
@@ -754,6 +797,13 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
754797 emit ( . copyStack( Instruction . CopyStackOperand ( source: Int32 ( source) , dest: Int32 ( dest) ) ) )
755798 }
756799
800+ private mutating func preserveOnStack( depth: Int ) {
801+ preserveLocalsOnStack ( depth: depth)
802+ for (source, dest) in valueStack. preserveConstsOnStack ( depth: depth) {
803+ emitCopyStack ( from: source, to: dest)
804+ }
805+ }
806+
757807 private mutating func preserveLocalsOnStack( _ localIndex: LocalIndex ) {
758808 for copyTo in valueStack. preserveLocalsOnStack ( localIndex) {
759809 emitCopyStack ( from: localReg ( localIndex) , to: copyTo)
@@ -800,6 +850,8 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
800850 return register
801851 case . local( let index) :
802852 return stackLayout. localReg ( index)
853+ case . const( let index, _) :
854+ return stackLayout. constReg ( index)
803855 }
804856 }
805857 private mutating func ensureOnStack( _ source: ValueSource ) -> VReg {
@@ -810,6 +862,9 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
810862 case . local( let localIndex) :
811863 emitCopyStack ( from: localReg ( localIndex) , to: copyTo)
812864 return copyTo
865+ case . const( let index, _) :
866+ emitCopyStack ( from: stackLayout. constReg ( index) , to: copyTo)
867+ return copyTo
813868 }
814869 }
815870 private mutating func popOperand( _ type: ValueType ) throws -> ValueSource ? {
@@ -837,7 +892,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
837892 }
838893
839894 private mutating func visitReturnLike( ) throws {
840- preserveLocalsOnStack ( depth: self . type. results. count)
895+ preserveOnStack ( depth: self . type. results. count)
841896 for (index, resultType) in self . type. results. enumerated ( ) . reversed ( ) {
842897 let source = ensureOnVReg ( try valueStack. pop ( resultType) )
843898 let dest = returnReg ( index)
@@ -846,7 +901,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
846901 }
847902
848903 private mutating func copyOnBranch( targetFrame frame: ControlStack . ControlFrame ) throws {
849- preserveLocalsOnStack ( depth: Int ( valueStack. height - frame. stackHeight) )
904+ preserveOnStack ( depth: min ( Int ( frame . copyCount ) , valueStack. height - frame. stackHeight) )
850905 let copyCount = VReg ( frame. copyCount)
851906 let sourceBase = valueStack. stackRegBase + VReg( valueStack. height)
852907 let destBase = valueStack. stackRegBase + VReg( frame. stackHeight)
@@ -889,9 +944,11 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
889944 for (idx, instruction) in instructions. enumerated ( ) {
890945 buffer [ idx] = instruction
891946 }
947+ let constants = allocator. allocateConstants ( self . constantSlots)
892948 return InstructionSequence (
893949 instructions: buffer,
894- maxStackHeight: Int ( valueStack. stackRegBase) + valueStack. maxHeight
950+ maxStackHeight: Int ( valueStack. stackRegBase) + valueStack. maxHeight,
951+ constants: constants
895952 )
896953 }
897954
@@ -933,14 +990,16 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
933990 _ = try valueStack. pushLocal ( localIndex, locals: & locals)
934991 case . vreg, nil :
935992 _ = valueStack. push ( param)
993+ case . const( let index, let type) :
994+ valueStack. pushConst ( index, type: type)
936995 }
937996 }
938997 controlStack. pushFrame ( ControlStack . ControlFrame ( blockType: blockType, stackHeight: stackHeight, continuation: endLabel, kind: . block) )
939998 }
940999
9411000 mutating func visitLoop( blockType: WasmParser . BlockType ) throws -> Output {
9421001 let blockType = try module. resolveBlockType ( blockType)
943- preserveLocalsOnStack ( depth: blockType. parameters. count)
1002+ preserveOnStack ( depth: blockType. parameters. count)
9441003 iseqBuilder. resetLastEmission ( )
9451004 for param in blockType. parameters. reversed ( ) {
9461005 _ = try popOperand ( param)
@@ -957,6 +1016,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
9571016 // Pop condition value
9581017 let condition = try popVRegOperand ( . i32)
9591018 let blockType = try module. resolveBlockType ( blockType)
1019+ preserveOnStack ( depth: blockType. parameters. count)
9601020 let endLabel = iseqBuilder. allocLabel ( )
9611021 let elseLabel = iseqBuilder. allocLabel ( )
9621022 for param in blockType. parameters. reversed ( ) {
@@ -990,7 +1050,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
9901050 guard case let . if( elseLabel, endLabel) = frame. kind else {
9911051 throw TranslationError ( " Expected `if` control frame on top of the stack for `else` but got \( frame) " )
9921052 }
993- preserveLocalsOnStack ( depth: valueStack. height - frame. stackHeight)
1053+ preserveOnStack ( depth: valueStack. height - frame. stackHeight)
9941054 try controlStack. resetReachability ( )
9951055 iseqBuilder. resetLastEmission ( )
9961056 iseqBuilder. emitWithLabel ( endLabel) { _, selfPC, endPC in
@@ -1033,7 +1093,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
10331093
10341094 // NOTE: `valueStack.height - poppedFrame.stackHeight` is usually the same as `poppedFrame.copyCount`
10351095 // but it's not always the case when this block is already unreachable.
1036- preserveLocalsOnStack ( depth: Int ( valueStack. height - poppedFrame. stackHeight) )
1096+ preserveOnStack ( depth: Int ( valueStack. height - poppedFrame. stackHeight) )
10371097 switch poppedFrame. kind {
10381098 case . block:
10391099 try iseqBuilder. pinLabelHere ( poppedFrame. continuation)
@@ -1115,7 +1175,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
11151175 }
11161176 return
11171177 }
1118- preserveLocalsOnStack ( depth: valueStack. height - frame. stackHeight)
1178+ preserveOnStack ( depth: valueStack. height - frame. stackHeight)
11191179
11201180 // If branch taken, fallthrough to landing pad, copy stack values
11211181 // then branch to the actual place
@@ -1155,7 +1215,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
11551215
11561216 let defaultFrame = try controlStack. branchTarget ( relativeDepth: targets. defaultIndex)
11571217
1158- preserveLocalsOnStack ( depth: Int ( defaultFrame. copyCount) )
1218+ preserveOnStack ( depth: Int ( defaultFrame. copyCount) )
11591219 let allLabelIndices = targets. labelIndices + [ targets. defaultIndex]
11601220 let tableBuffer = allocator. allocateBrTable ( capacity: allLabelIndices. count)
11611221 let operand = Instruction . BrTable (
@@ -1503,7 +1563,18 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
15031563 }
15041564 }
15051565
1566+ private mutating func allocateConstSlot( _ type: ValueType , _ value: Value ) -> Int ? {
1567+ guard constantSlots. count < stackLayout. constantSlotSize else { return nil }
1568+ let constSlotIndex = constantSlots. count
1569+ constantSlots. append ( UntypedValue ( value) )
1570+ return constSlotIndex
1571+ }
1572+
15061573 private mutating func visitConst( _ type: ValueType , _ value: Value ) {
1574+ if let constSlotIndex = allocateConstSlot ( type, value) {
1575+ valueStack. pushConst ( constSlotIndex, type: type)
1576+ return
1577+ }
15071578 let value = UntypedValue ( value)
15081579 let is32Bit = type == . i32 || type == . f32
15091580 if is32Bit {
0 commit comments