Skip to content

Commit db9c104

Browse files
Constant Pool
1 parent 5d3bdb5 commit db9c104

File tree

4 files changed

+98
-18
lines changed

4 files changed

+98
-18
lines changed

Sources/WasmKit/Execution/Execution.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ struct Execution {
4949
}
5050
// Initialize the locals with zeros (all types of value have the same representation)
5151
newSp.initialize(repeating: UntypedValue.default.storage, count: numberOfNonParameterLocals)
52+
if let constants = iseq.constants.baseAddress {
53+
let count = iseq.constants.count
54+
newSp.advanced(by: numberOfNonParameterLocals).withMemoryRebound(to: UntypedValue.self, capacity: count) {
55+
$0.initialize(from: constants, count: count)
56+
}
57+
}
5258
newSp[-1] = UInt64(UInt(bitPattern: sp))
5359
newSp[-2] = UInt64(UInt(bitPattern: returnPC))
5460
newSp[-3] = UInt64(UInt(bitPattern: instance.bitPattern))

Sources/WasmKit/Execution/Instructions/Expression.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ struct InstructionSequence: Equatable {
66
/// This height does not count the locals.
77
let maxStackHeight: Int
88

9-
init(instructions: UnsafeMutableBufferPointer<CodeSlot>, maxStackHeight: Int) {
9+
let constants: UnsafeBufferPointer<UntypedValue>
10+
11+
init(instructions: UnsafeMutableBufferPointer<CodeSlot>, maxStackHeight: Int, constants: UnsafeBufferPointer<UntypedValue>) {
1012
self.instructions = instructions
1113
self.maxStackHeight = maxStackHeight
14+
self.constants = constants
1215
}
1316

1417
var baseAddress: UnsafeMutablePointer<CodeSlot> {

Sources/WasmKit/Translator.swift

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

177188
struct 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 {

Utilities/Sources/VMSpec.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,4 @@ extension VMGen {
418418
}
419419

420420
static let instructions: [Instruction] = buildInstructions()
421-
}
421+
}

0 commit comments

Comments
 (0)