Skip to content

Commit 8eed37f

Browse files
authored
Threads: parsing of atomic load/store instructions (#205)
See [threads proposal explainer](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md) for more details. These instructions are shimmed with existing non-atomic loads and stores for now, which seems reasonable as we can't spawn any threads yet. I've also added `#include <stdatomic.h>` placeholder to `_CWasmKit`, which we'll use for the actual implementation in a future PR.
1 parent d9b56a7 commit 8eed37f

File tree

13 files changed

+566
-56
lines changed

13 files changed

+566
-56
lines changed

Sources/WAT/BinaryInstructionEncoder.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ extension BinaryInstructionEncoder {
125125
case .i64Load16U: opcode = [0x33]
126126
case .i64Load32S: opcode = [0x34]
127127
case .i64Load32U: opcode = [0x35]
128+
case .i32AtomicLoad: opcode = [0xFE, 0x10]
129+
case .i64AtomicLoad: opcode = [0xFE, 0x11]
130+
case .i32AtomicLoad8U: opcode = [0xFE, 0x12]
131+
case .i32AtomicLoad16U: opcode = [0xFE, 0x13]
132+
case .i64AtomicLoad8U: opcode = [0xFE, 0x14]
133+
case .i64AtomicLoad16U: opcode = [0xFE, 0x15]
134+
case .i64AtomicLoad32U: opcode = [0xFE, 0x16]
128135
}
129136

130137
try encodeInstruction(opcode)
@@ -142,6 +149,13 @@ extension BinaryInstructionEncoder {
142149
case .i64Store8: opcode = [0x3C]
143150
case .i64Store16: opcode = [0x3D]
144151
case .i64Store32: opcode = [0x3E]
152+
case .i32AtomicStore: opcode = [0xFE, 0x17]
153+
case .i64AtomicStore: opcode = [0xFE, 0x18]
154+
case .i32AtomicStore8: opcode = [0xFE, 0x19]
155+
case .i32AtomicStore16: opcode = [0xFE, 0x1A]
156+
case .i64AtomicStore8: opcode = [0xFE, 0x1B]
157+
case .i64AtomicStore16: opcode = [0xFE, 0x1C]
158+
case .i64AtomicStore32: opcode = [0xFE, 0x1D]
145159
}
146160

147161
try encodeInstruction(opcode)
@@ -392,4 +406,5 @@ extension BinaryInstructionEncoder {
392406
try encodeInstruction([0xFC, 0x10])
393407
try encodeImmediates(table: table)
394408
}
409+
mutating func visitAtomicFence() throws { try encodeInstruction([0xFE, 0x03, 0x00]) }
395410
}

Sources/WAT/ParseTextInstruction.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,49 @@ func parseTextInstruction<V: InstructionVisitor>(keyword: String, expressionPars
332332
case "i64.trunc_sat_f32_u": return { return try $0.visitConversion(.i64TruncSatF32U) }
333333
case "i64.trunc_sat_f64_s": return { return try $0.visitConversion(.i64TruncSatF64S) }
334334
case "i64.trunc_sat_f64_u": return { return try $0.visitConversion(.i64TruncSatF64U) }
335+
case "atomic.fence": return { return try $0.visitAtomicFence() }
336+
case "i32.atomic.load":
337+
let (memarg) = try expressionParser.visitLoad(.i32AtomicLoad, wat: &wat)
338+
return { return try $0.visitLoad(.i32AtomicLoad, memarg: memarg) }
339+
case "i64.atomic.load":
340+
let (memarg) = try expressionParser.visitLoad(.i64AtomicLoad, wat: &wat)
341+
return { return try $0.visitLoad(.i64AtomicLoad, memarg: memarg) }
342+
case "i32.atomic.load8_u":
343+
let (memarg) = try expressionParser.visitLoad(.i32AtomicLoad8U, wat: &wat)
344+
return { return try $0.visitLoad(.i32AtomicLoad8U, memarg: memarg) }
345+
case "i32.atomic.load16_u":
346+
let (memarg) = try expressionParser.visitLoad(.i32AtomicLoad16U, wat: &wat)
347+
return { return try $0.visitLoad(.i32AtomicLoad16U, memarg: memarg) }
348+
case "i64.atomic.load8_u":
349+
let (memarg) = try expressionParser.visitLoad(.i64AtomicLoad8U, wat: &wat)
350+
return { return try $0.visitLoad(.i64AtomicLoad8U, memarg: memarg) }
351+
case "i64.atomic.load16_u":
352+
let (memarg) = try expressionParser.visitLoad(.i64AtomicLoad16U, wat: &wat)
353+
return { return try $0.visitLoad(.i64AtomicLoad16U, memarg: memarg) }
354+
case "i64.atomic.load32_u":
355+
let (memarg) = try expressionParser.visitLoad(.i64AtomicLoad32U, wat: &wat)
356+
return { return try $0.visitLoad(.i64AtomicLoad32U, memarg: memarg) }
357+
case "i32.atomic.store":
358+
let (memarg) = try expressionParser.visitStore(.i32AtomicStore, wat: &wat)
359+
return { return try $0.visitStore(.i32AtomicStore, memarg: memarg) }
360+
case "i64.atomic.store":
361+
let (memarg) = try expressionParser.visitStore(.i64AtomicStore, wat: &wat)
362+
return { return try $0.visitStore(.i64AtomicStore, memarg: memarg) }
363+
case "i32.atomic.store8":
364+
let (memarg) = try expressionParser.visitStore(.i32AtomicStore8, wat: &wat)
365+
return { return try $0.visitStore(.i32AtomicStore8, memarg: memarg) }
366+
case "i32.atomic.store16":
367+
let (memarg) = try expressionParser.visitStore(.i32AtomicStore16, wat: &wat)
368+
return { return try $0.visitStore(.i32AtomicStore16, memarg: memarg) }
369+
case "i64.atomic.store8":
370+
let (memarg) = try expressionParser.visitStore(.i64AtomicStore8, wat: &wat)
371+
return { return try $0.visitStore(.i64AtomicStore8, memarg: memarg) }
372+
case "i64.atomic.store16":
373+
let (memarg) = try expressionParser.visitStore(.i64AtomicStore16, wat: &wat)
374+
return { return try $0.visitStore(.i64AtomicStore16, memarg: memarg) }
375+
case "i64.atomic.store32":
376+
let (memarg) = try expressionParser.visitStore(.i64AtomicStore32, wat: &wat)
377+
return { return try $0.visitStore(.i64AtomicStore32, memarg: memarg) }
335378
default: return nil
336379
}
337380
}

Sources/WasmKit/Execution/DispatchInstruction.swift

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,20 @@ extension Execution {
213213
case 197: return self.execute_onEnter(sp: &sp, pc: &pc, md: &md, ms: &ms)
214214
case 198: return self.execute_onExit(sp: &sp, pc: &pc, md: &md, ms: &ms)
215215
case 199: return try self.execute_breakpoint(sp: &sp, pc: &pc, md: &md, ms: &ms)
216+
case 200: return try self.execute_i32AtomicLoad(sp: &sp, pc: &pc, md: &md, ms: &ms)
217+
case 201: return try self.execute_i64AtomicLoad(sp: &sp, pc: &pc, md: &md, ms: &ms)
218+
case 202: return try self.execute_i32AtomicLoad8U(sp: &sp, pc: &pc, md: &md, ms: &ms)
219+
case 203: return try self.execute_i32AtomicLoad16U(sp: &sp, pc: &pc, md: &md, ms: &ms)
220+
case 204: return try self.execute_i64AtomicLoad8U(sp: &sp, pc: &pc, md: &md, ms: &ms)
221+
case 205: return try self.execute_i64AtomicLoad16U(sp: &sp, pc: &pc, md: &md, ms: &ms)
222+
case 206: return try self.execute_i64AtomicLoad32U(sp: &sp, pc: &pc, md: &md, ms: &ms)
223+
case 207: return try self.execute_i32AtomicStore(sp: &sp, pc: &pc, md: &md, ms: &ms)
224+
case 208: return try self.execute_i64AtomicStore(sp: &sp, pc: &pc, md: &md, ms: &ms)
225+
case 209: return try self.execute_i32AtomicStore8(sp: &sp, pc: &pc, md: &md, ms: &ms)
226+
case 210: return try self.execute_i32AtomicStore16(sp: &sp, pc: &pc, md: &md, ms: &ms)
227+
case 211: return try self.execute_i64AtomicStore8(sp: &sp, pc: &pc, md: &md, ms: &ms)
228+
case 212: return try self.execute_i64AtomicStore16(sp: &sp, pc: &pc, md: &md, ms: &ms)
229+
case 213: return try self.execute_i64AtomicStore32(sp: &sp, pc: &pc, md: &md, ms: &ms)
216230
default: preconditionFailure("Unknown instruction!?")
217231

218232
}
@@ -1802,6 +1816,118 @@ extension Execution {
18021816
(pc.pointee, next) = try self.breakpoint(sp: &sp.pointee, pc: pc.pointee)
18031817
return next
18041818
}
1819+
@_silgen_name("wasmkit_execute_i32AtomicLoad") @inline(__always)
1820+
mutating func execute_i32AtomicLoad(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1821+
let immediate = Instruction.LoadOperand.load(from: &pc.pointee)
1822+
try memoryLoad(sp: sp.pointee, md: md.pointee, ms: ms.pointee, loadOperand: immediate, loadAs: UInt32.self, castToValue: { .i32($0) })
1823+
let next = pc.pointee.pointee
1824+
pc.pointee = pc.pointee.advanced(by: 1)
1825+
return next
1826+
}
1827+
@_silgen_name("wasmkit_execute_i64AtomicLoad") @inline(__always)
1828+
mutating func execute_i64AtomicLoad(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1829+
let immediate = Instruction.LoadOperand.load(from: &pc.pointee)
1830+
try memoryLoad(sp: sp.pointee, md: md.pointee, ms: ms.pointee, loadOperand: immediate, loadAs: UInt64.self, castToValue: { .i64($0) })
1831+
let next = pc.pointee.pointee
1832+
pc.pointee = pc.pointee.advanced(by: 1)
1833+
return next
1834+
}
1835+
@_silgen_name("wasmkit_execute_i32AtomicLoad8U") @inline(__always)
1836+
mutating func execute_i32AtomicLoad8U(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1837+
let immediate = Instruction.LoadOperand.load(from: &pc.pointee)
1838+
try memoryLoad(sp: sp.pointee, md: md.pointee, ms: ms.pointee, loadOperand: immediate, loadAs: UInt8.self, castToValue: { .i32(UInt32($0)) })
1839+
let next = pc.pointee.pointee
1840+
pc.pointee = pc.pointee.advanced(by: 1)
1841+
return next
1842+
}
1843+
@_silgen_name("wasmkit_execute_i32AtomicLoad16U") @inline(__always)
1844+
mutating func execute_i32AtomicLoad16U(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1845+
let immediate = Instruction.LoadOperand.load(from: &pc.pointee)
1846+
try memoryLoad(sp: sp.pointee, md: md.pointee, ms: ms.pointee, loadOperand: immediate, loadAs: UInt16.self, castToValue: { .i32(UInt32($0)) })
1847+
let next = pc.pointee.pointee
1848+
pc.pointee = pc.pointee.advanced(by: 1)
1849+
return next
1850+
}
1851+
@_silgen_name("wasmkit_execute_i64AtomicLoad8U") @inline(__always)
1852+
mutating func execute_i64AtomicLoad8U(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1853+
let immediate = Instruction.LoadOperand.load(from: &pc.pointee)
1854+
try memoryLoad(sp: sp.pointee, md: md.pointee, ms: ms.pointee, loadOperand: immediate, loadAs: UInt8.self, castToValue: { .i64(UInt64($0)) })
1855+
let next = pc.pointee.pointee
1856+
pc.pointee = pc.pointee.advanced(by: 1)
1857+
return next
1858+
}
1859+
@_silgen_name("wasmkit_execute_i64AtomicLoad16U") @inline(__always)
1860+
mutating func execute_i64AtomicLoad16U(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1861+
let immediate = Instruction.LoadOperand.load(from: &pc.pointee)
1862+
try memoryLoad(sp: sp.pointee, md: md.pointee, ms: ms.pointee, loadOperand: immediate, loadAs: UInt16.self, castToValue: { .i64(UInt64($0)) })
1863+
let next = pc.pointee.pointee
1864+
pc.pointee = pc.pointee.advanced(by: 1)
1865+
return next
1866+
}
1867+
@_silgen_name("wasmkit_execute_i64AtomicLoad32U") @inline(__always)
1868+
mutating func execute_i64AtomicLoad32U(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1869+
let immediate = Instruction.LoadOperand.load(from: &pc.pointee)
1870+
try memoryLoad(sp: sp.pointee, md: md.pointee, ms: ms.pointee, loadOperand: immediate, loadAs: UInt32.self, castToValue: { .i64(UInt64($0)) })
1871+
let next = pc.pointee.pointee
1872+
pc.pointee = pc.pointee.advanced(by: 1)
1873+
return next
1874+
}
1875+
@_silgen_name("wasmkit_execute_i32AtomicStore") @inline(__always)
1876+
mutating func execute_i32AtomicStore(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1877+
let immediate = Instruction.StoreOperand.load(from: &pc.pointee)
1878+
try memoryStore(sp: sp.pointee, md: md.pointee, ms: ms.pointee, storeOperand: immediate, castFromValue: { $0.i32 })
1879+
let next = pc.pointee.pointee
1880+
pc.pointee = pc.pointee.advanced(by: 1)
1881+
return next
1882+
}
1883+
@_silgen_name("wasmkit_execute_i64AtomicStore") @inline(__always)
1884+
mutating func execute_i64AtomicStore(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1885+
let immediate = Instruction.StoreOperand.load(from: &pc.pointee)
1886+
try memoryStore(sp: sp.pointee, md: md.pointee, ms: ms.pointee, storeOperand: immediate, castFromValue: { $0.i64 })
1887+
let next = pc.pointee.pointee
1888+
pc.pointee = pc.pointee.advanced(by: 1)
1889+
return next
1890+
}
1891+
@_silgen_name("wasmkit_execute_i32AtomicStore8") @inline(__always)
1892+
mutating func execute_i32AtomicStore8(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1893+
let immediate = Instruction.StoreOperand.load(from: &pc.pointee)
1894+
try memoryStore(sp: sp.pointee, md: md.pointee, ms: ms.pointee, storeOperand: immediate, castFromValue: { UInt8(truncatingIfNeeded: $0.i32) })
1895+
let next = pc.pointee.pointee
1896+
pc.pointee = pc.pointee.advanced(by: 1)
1897+
return next
1898+
}
1899+
@_silgen_name("wasmkit_execute_i32AtomicStore16") @inline(__always)
1900+
mutating func execute_i32AtomicStore16(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1901+
let immediate = Instruction.StoreOperand.load(from: &pc.pointee)
1902+
try memoryStore(sp: sp.pointee, md: md.pointee, ms: ms.pointee, storeOperand: immediate, castFromValue: { UInt16(truncatingIfNeeded: $0.i32) })
1903+
let next = pc.pointee.pointee
1904+
pc.pointee = pc.pointee.advanced(by: 1)
1905+
return next
1906+
}
1907+
@_silgen_name("wasmkit_execute_i64AtomicStore8") @inline(__always)
1908+
mutating func execute_i64AtomicStore8(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1909+
let immediate = Instruction.StoreOperand.load(from: &pc.pointee)
1910+
try memoryStore(sp: sp.pointee, md: md.pointee, ms: ms.pointee, storeOperand: immediate, castFromValue: { UInt8(truncatingIfNeeded: $0.i64) })
1911+
let next = pc.pointee.pointee
1912+
pc.pointee = pc.pointee.advanced(by: 1)
1913+
return next
1914+
}
1915+
@_silgen_name("wasmkit_execute_i64AtomicStore16") @inline(__always)
1916+
mutating func execute_i64AtomicStore16(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1917+
let immediate = Instruction.StoreOperand.load(from: &pc.pointee)
1918+
try memoryStore(sp: sp.pointee, md: md.pointee, ms: ms.pointee, storeOperand: immediate, castFromValue: { UInt16(truncatingIfNeeded: $0.i64) })
1919+
let next = pc.pointee.pointee
1920+
pc.pointee = pc.pointee.advanced(by: 1)
1921+
return next
1922+
}
1923+
@_silgen_name("wasmkit_execute_i64AtomicStore32") @inline(__always)
1924+
mutating func execute_i64AtomicStore32(sp: UnsafeMutablePointer<Sp>, pc: UnsafeMutablePointer<Pc>, md: UnsafeMutablePointer<Md>, ms: UnsafeMutablePointer<Ms>) throws -> CodeSlot {
1925+
let immediate = Instruction.StoreOperand.load(from: &pc.pointee)
1926+
try memoryStore(sp: sp.pointee, md: md.pointee, ms: ms.pointee, storeOperand: immediate, castFromValue: { UInt32(truncatingIfNeeded: $0.i64) })
1927+
let next = pc.pointee.pointee
1928+
pc.pointee = pc.pointee.advanced(by: 1)
1929+
return next
1930+
}
18051931
}
18061932

18071933
extension Instruction {

0 commit comments

Comments
 (0)