Skip to content

Commit 6a13d68

Browse files
LiedtkeV8-internal LUCI CQ
authored andcommitted
[wasm] Add parameters and labels to if and else blocks
Change-Id: I2af6c8b312f697982e7c0f7f88572d82e9e81d68 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/7967829 Reviewed-by: Carl Smith <[email protected]> Commit-Queue: Matthias Liedtke <[email protected]>
1 parent 34c4913 commit 6a13d68

File tree

11 files changed

+211
-40
lines changed

11 files changed

+211
-40
lines changed

Sources/Fuzzilli/Base/ProgramBuilder.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3261,6 +3261,14 @@ public class ProgramBuilder {
32613261
b.emit(WasmEndIf())
32623262
}
32633263

3264+
public func wasmBuildIfElse(_ condition: Variable, signature: Signature, args: [Variable], ifBody: (Variable, [Variable]) -> Void, elseBody: (Variable, [Variable]) -> Void) {
3265+
let beginBlock = b.emit(WasmBeginIf(with: signature), withInputs: args + [condition])
3266+
ifBody(beginBlock.innerOutput(0), Array(beginBlock.innerOutputs(1...)))
3267+
let elseBlock = b.emit(WasmBeginElse(with: signature))
3268+
elseBody(elseBlock.innerOutput(0), Array(elseBlock.innerOutputs(1...)))
3269+
b.emit(WasmEndIf())
3270+
}
3271+
32643272
// The first output of this block is a label variable, which is just there to explicitly mark control-flow and allow branches.
32653273
public func wasmBuildLoop(with signature: Signature, body: (Variable, [Variable]) -> Void) {
32663274
let instr = b.emit(WasmBeginLoop(with: signature))

Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ public let codeGeneratorWeights = [
268268
// Control Flow Generators
269269
"WasmFunctionGenerator": 30,
270270
"WasmIfElseGenerator": 15,
271+
"WasmIfElseWithSignatureGenerator": 10,
271272
"WasmReturnGenerator": 15,
272273
"WasmBlockGenerator": 8,
273274
"WasmBlockWithSignatureGenerator": 8,

Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,18 @@ public let WasmCodeGenerators: [CodeGenerator] = [
633633
}
634634
},
635635

636+
RecursiveCodeGenerator("WasmIfElseWithSignatureGenerator", inContext: .wasmFunction, inputs: .required(.wasmi32)) { b, conditionVar in
637+
let function = b.currentWasmModule.currentWasmFunction
638+
// Choose a few random wasm values as arguments if available.
639+
let args = (0..<5).map {_ in b.findVariable {b.type(of: $0).Is(.wasmPrimitive)}}.filter {$0 != nil}.map {$0!}
640+
let parameters = args.map {arg in Parameter.plain(b.type(of: arg))}
641+
function.wasmBuildIfElse(conditionVar, signature: parameters => .nothing, args: args) { label, args in
642+
b.buildRecursive(block: 1, of: 2, n: 4)
643+
} elseBody: { label, args in
644+
b.buildRecursive(block: 2, of: 2, n: 4)
645+
}
646+
},
647+
636648
CodeGenerator("WasmSelectGenerator", inContext: .wasmFunction, inputs: .required(.wasmi32)) { b, condition in
637649
let function = b.currentWasmModule.currentWasmFunction
638650
let supportedTypes : ILType = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 | .wasmExternRef

Sources/Fuzzilli/FuzzIL/Instruction.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,10 +1242,16 @@ extension Instruction: ProtobufConvertible {
12421242
$0.wasmBranch = Fuzzilli_Protobuf_WasmBranch()
12431243
case .wasmBranchIf(_):
12441244
$0.wasmBranchIf = Fuzzilli_Protobuf_WasmBranchIf()
1245-
case .wasmBeginIf(_):
1246-
$0.wasmBeginIf = Fuzzilli_Protobuf_WasmBeginIf()
1247-
case .wasmBeginElse(_):
1248-
$0.wasmBeginElse = Fuzzilli_Protobuf_WasmBeginElse()
1245+
case .wasmBeginIf(let op):
1246+
$0.wasmBeginIf = Fuzzilli_Protobuf_WasmBeginIf.with {
1247+
$0.parameters = convertParametersToWasmTypeEnums(op.signature.parameters)
1248+
$0.returnType = ILTypeToWasmTypeEnum(op.signature.outputType)
1249+
}
1250+
case .wasmBeginElse(let op):
1251+
$0.wasmBeginElse = Fuzzilli_Protobuf_WasmBeginElse.with {
1252+
$0.parameters = convertParametersToWasmTypeEnums(op.signature.parameters)
1253+
$0.returnType = ILTypeToWasmTypeEnum(op.signature.outputType)
1254+
}
12491255
case .wasmEndIf(_):
12501256
$0.wasmEndIf = Fuzzilli_Protobuf_WasmEndIf()
12511257
case .wasmNop(_):
@@ -2031,10 +2037,16 @@ extension Instruction: ProtobufConvertible {
20312037
op = WasmBranch()
20322038
case .wasmBranchIf(_):
20332039
op = WasmBranchIf()
2034-
case .wasmBeginIf(_):
2035-
op = WasmBeginIf()
2036-
case .wasmBeginElse(_):
2037-
op = WasmBeginElse()
2040+
case .wasmBeginIf(let p):
2041+
let parameters: [Parameter] = p.parameters.map({ param in
2042+
Parameter.plain(WasmTypeEnumToILType(param))
2043+
})
2044+
op = WasmBeginIf(with: parameters => WasmTypeEnumToILType(p.returnType))
2045+
case .wasmBeginElse(let p):
2046+
let parameters: [Parameter] = p.parameters.map({ param in
2047+
Parameter.plain(WasmTypeEnumToILType(param))
2048+
})
2049+
op = WasmBeginElse(with: parameters => WasmTypeEnumToILType(p.returnType))
20382050
case .wasmEndIf(_):
20392051
op = WasmEndIf()
20402052
case .wasmNop(_):

Sources/Fuzzilli/FuzzIL/JSTyper.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ public struct JSTyper: Analyzer {
111111
activeWasmModuleDefinition!.methodSignatures.append((instr.output, signature))
112112
case .wasmBeginBlock(_),
113113
.wasmBeginLoop(_),
114+
.wasmBeginIf(_),
115+
.wasmBeginElse(_),
114116
.wasmBeginTry(_),
115117
.wasmBeginCatch(_),
116118
.wasmBeginTryDelegate(_):

Sources/Fuzzilli/FuzzIL/WasmOperations.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -967,17 +967,28 @@ final class WasmEndBlock: WasmOperation {
967967

968968
final class WasmBeginIf: WasmOperation {
969969
override var opcode: Opcode { .wasmBeginIf(self) }
970+
let signature: Signature
970971

971-
init() {
972-
super.init(inputTypes: [.wasmi32], attributes: [.isBlockStart, .propagatesSurroundingContext, .isNotInputMutable], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock])
972+
init(with signature: Signature = [] => .nothing) {
973+
self.signature = signature
974+
let parameterTypes = signature.parameters.convertPlainToILTypes()
975+
// TODO(mliedtke): Why does this set .isNotInputMutable? Try to remove it and see if the WasmLifter failure rate is affected.
976+
977+
// Note that the condition is the last input! This is due to how lifting works for the wasm
978+
// value stack and that the condition is the first value to be removed from the stack, so
979+
// it needs to be the last one pushed to it.
980+
super.init(inputTypes: parameterTypes + [.wasmi32], outputType: signature.outputType, innerOutputTypes: [.label] + parameterTypes, attributes: [.isBlockStart, .propagatesSurroundingContext, .isNotInputMutable], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock])
973981
}
974982
}
975983

976984
final class WasmBeginElse: WasmOperation {
977985
override var opcode: Opcode { .wasmBeginElse(self) }
986+
let signature: Signature
978987

979-
init() {
980-
super.init(attributes: [.isBlockStart, .isBlockEnd, .propagatesSurroundingContext], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock])
988+
init(with signature: Signature = [] => .nothing) {
989+
self.signature = signature
990+
let parameterTypes = signature.parameters.convertPlainToILTypes()
991+
super.init(outputType: signature.outputType, innerOutputTypes: [.label] + parameterTypes, attributes: [.isBlockStart, .isBlockEnd, .propagatesSurroundingContext], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock])
981992
}
982993
}
983994

Sources/Fuzzilli/Lifting/FuzzILLifter.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,13 +1047,18 @@ public class FuzzILLifter: Lifter {
10471047
case .wasmBranchIf(_):
10481048
w.emit("wasmBranchIf \(instr.input(1)), \(instr.input(0))")
10491049

1050-
case .wasmBeginIf(_):
1051-
w.emit("wasmBeginIf \(instr.input(0))")
1050+
case .wasmBeginIf(let op):
1051+
let inputs = instr.inputs.map(lift).joined(separator: ", ")
1052+
if instr.numOutputs > 0 {
1053+
w.emit("\(output()) <- wasmBeginIf L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))] (\(op.signature)) [\(inputs)]")
1054+
} else {
1055+
w.emit("wasmBeginIf L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))] (\(op.signature)) [\(inputs)]")
1056+
}
10521057
w.increaseIndentionLevel()
10531058

1054-
case .wasmBeginElse(_):
1059+
case .wasmBeginElse(let op):
10551060
w.decreaseIndentionLevel()
1056-
w.emit("wasmBeginElse")
1061+
w.emit("wasmBeginElse L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))] (\(op.signature))")
10571062
w.increaseIndentionLevel()
10581063

10591064
case .wasmEndIf(_):

Sources/Fuzzilli/Lifting/WasmLifter.swift

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -866,11 +866,21 @@ public class WasmLifter {
866866

867867
switch instr.op.opcode {
868868
case .wasmBeginBlock(let op):
869-
// TODO(mliedtke): Repeat this for loops and if-else blocks.
869+
// TODO(mliedtke): Repeat this for loops.
870870
registerSignature(op.signature)
871871
self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth
872872
// Needs typer analysis
873873
return true
874+
case .wasmBeginIf(let op):
875+
registerSignature(op.signature)
876+
self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth
877+
// Needs typer analysis
878+
return true
879+
case .wasmBeginElse(_):
880+
// Note: We need to subtract one because the begin else block closes the if block before opening the else block!
881+
self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth - 1
882+
// Needs typer analysis
883+
return true
874884
case .wasmBeginTry(let op):
875885
registerSignature(op.signature)
876886
self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth
@@ -954,20 +964,12 @@ public class WasmLifter {
954964

955965
// TODO(mliedtke): Make this an attribute.
956966
// Instruction has to be a glue instruction now, maybe add an attribute to the instruction that it may have non-wasm inputs, i.e. inputs that do not have a local slot.
957-
if instr.op is WasmLoadGlobal ||
958-
instr.op is WasmStoreGlobal ||
959-
instr.op is WasmJsCall ||
960-
instr.op is WasmMemoryStore ||
961-
instr.op is WasmMemoryLoad ||
962-
instr.op is WasmI64x2LoadSplat ||
963-
instr.op is WasmTableGet ||
964-
instr.op is WasmTableSet ||
965-
instr.op is WasmBeginCatch ||
966-
instr.op is WasmThrow ||
967-
instr.op is WasmRethrow ||
968-
instr.op is WasmBeginBlock ||
969-
instr.op is WasmBeginTry ||
970-
instr.op is WasmBeginTryDelegate {
967+
if instr.op is WasmLoadGlobal || instr.op is WasmStoreGlobal || instr.op is WasmJsCall
968+
|| instr.op is WasmMemoryStore || instr.op is WasmMemoryLoad || instr.op is WasmTableGet
969+
|| instr.op is WasmTableSet || instr.op is WasmBeginCatch || instr.op is WasmThrow
970+
|| instr.op is WasmRethrow || instr.op is WasmBeginBlock || instr.op is WasmBeginTry
971+
|| instr.op is WasmI64x2LoadSplat || instr.op is WasmBeginTryDelegate
972+
|| instr.op is WasmBeginIf || instr.op is WasmBeginElse {
971973
continue
972974
}
973975
fatalError("unreachable")
@@ -995,7 +997,8 @@ public class WasmLifter {
995997
}
996998

997999
// TODO(mliedtke): Reuse this for handling parameters in loops, if-else, ...
998-
if instr.op is WasmBeginCatch || instr.op is WasmBeginBlock || instr.op is WasmBeginTry || instr.op is WasmBeginTryDelegate {
1000+
if instr.op is WasmBeginCatch || instr.op is WasmBeginBlock || instr.op is WasmBeginTry
1001+
|| instr.op is WasmBeginTryDelegate || instr.op is WasmBeginIf || instr.op is WasmBeginElse {
9991002
// As the parameters are pushed "in order" to the stack, they need to be popped in reverse order.
10001003
for innerOutput in instr.innerOutputs(1...).reversed() {
10011004
currentFunction!.spillLocal(forVariable: innerOutput)
@@ -1399,8 +1402,8 @@ public class WasmLifter {
13991402
case .wasmBranchIf(_):
14001403
let branchDepth = self.currentFunction!.variableAnalyzer.wasmBranchDepth - self.currentFunction!.labelBranchDepthMapping[wasmInstruction.input(0)]! - 1
14011404
return Data([0x0D]) + Leb128.unsignedEncode(branchDepth)
1402-
case .wasmBeginIf(_):
1403-
return Data([0x04] + [0x40])
1405+
case .wasmBeginIf(let op):
1406+
return Data([0x04] + Leb128.unsignedEncode(signatureIndexMap[op.signature]!))
14041407
case .wasmBeginElse(_):
14051408
// 0x05 is the else block instruction.
14061409
return Data([0x05])

Sources/Fuzzilli/Protobuf/operations.pb.swift

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4140,6 +4140,10 @@ public struct Fuzzilli_Protobuf_WasmBeginIf: Sendable {
41404140
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
41414141
// methods supported on all messages.
41424142

4143+
public var parameters: [Fuzzilli_Protobuf_WasmILType] = []
4144+
4145+
public var returnType: Fuzzilli_Protobuf_WasmILType = .consti32
4146+
41434147
public var unknownFields = SwiftProtobuf.UnknownStorage()
41444148

41454149
public init() {}
@@ -4150,6 +4154,10 @@ public struct Fuzzilli_Protobuf_WasmBeginElse: Sendable {
41504154
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
41514155
// methods supported on all messages.
41524156

4157+
public var parameters: [Fuzzilli_Protobuf_WasmILType] = []
4158+
4159+
public var returnType: Fuzzilli_Protobuf_WasmILType = .consti32
4160+
41534161
public var unknownFields = SwiftProtobuf.UnknownStorage()
41544162

41554163
public init() {}
@@ -12105,37 +12113,75 @@ extension Fuzzilli_Protobuf_WasmReassign: SwiftProtobuf.Message, SwiftProtobuf._
1210512113

1210612114
extension Fuzzilli_Protobuf_WasmBeginIf: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
1210712115
public static let protoMessageName: String = _protobuf_package + ".WasmBeginIf"
12108-
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
12116+
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
12117+
1: .same(proto: "parameters"),
12118+
2: .same(proto: "returnType"),
12119+
]
1210912120

1211012121
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
12111-
// Load everything into unknown fields
12112-
while try decoder.nextFieldNumber() != nil {}
12122+
while let fieldNumber = try decoder.nextFieldNumber() {
12123+
// The use of inline closures is to circumvent an issue where the compiler
12124+
// allocates stack space for every case branch when no optimizations are
12125+
// enabled. https://github.com/apple/swift-protobuf/issues/1034
12126+
switch fieldNumber {
12127+
case 1: try { try decoder.decodeRepeatedEnumField(value: &self.parameters) }()
12128+
case 2: try { try decoder.decodeSingularEnumField(value: &self.returnType) }()
12129+
default: break
12130+
}
12131+
}
1211312132
}
1211412133

1211512134
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
12135+
if !self.parameters.isEmpty {
12136+
try visitor.visitPackedEnumField(value: self.parameters, fieldNumber: 1)
12137+
}
12138+
if self.returnType != .consti32 {
12139+
try visitor.visitSingularEnumField(value: self.returnType, fieldNumber: 2)
12140+
}
1211612141
try unknownFields.traverse(visitor: &visitor)
1211712142
}
1211812143

1211912144
public static func ==(lhs: Fuzzilli_Protobuf_WasmBeginIf, rhs: Fuzzilli_Protobuf_WasmBeginIf) -> Bool {
12145+
if lhs.parameters != rhs.parameters {return false}
12146+
if lhs.returnType != rhs.returnType {return false}
1212012147
if lhs.unknownFields != rhs.unknownFields {return false}
1212112148
return true
1212212149
}
1212312150
}
1212412151

1212512152
extension Fuzzilli_Protobuf_WasmBeginElse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
1212612153
public static let protoMessageName: String = _protobuf_package + ".WasmBeginElse"
12127-
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
12154+
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
12155+
1: .same(proto: "parameters"),
12156+
2: .same(proto: "returnType"),
12157+
]
1212812158

1212912159
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
12130-
// Load everything into unknown fields
12131-
while try decoder.nextFieldNumber() != nil {}
12160+
while let fieldNumber = try decoder.nextFieldNumber() {
12161+
// The use of inline closures is to circumvent an issue where the compiler
12162+
// allocates stack space for every case branch when no optimizations are
12163+
// enabled. https://github.com/apple/swift-protobuf/issues/1034
12164+
switch fieldNumber {
12165+
case 1: try { try decoder.decodeRepeatedEnumField(value: &self.parameters) }()
12166+
case 2: try { try decoder.decodeSingularEnumField(value: &self.returnType) }()
12167+
default: break
12168+
}
12169+
}
1213212170
}
1213312171

1213412172
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
12173+
if !self.parameters.isEmpty {
12174+
try visitor.visitPackedEnumField(value: self.parameters, fieldNumber: 1)
12175+
}
12176+
if self.returnType != .consti32 {
12177+
try visitor.visitSingularEnumField(value: self.returnType, fieldNumber: 2)
12178+
}
1213512179
try unknownFields.traverse(visitor: &visitor)
1213612180
}
1213712181

1213812182
public static func ==(lhs: Fuzzilli_Protobuf_WasmBeginElse, rhs: Fuzzilli_Protobuf_WasmBeginElse) -> Bool {
12183+
if lhs.parameters != rhs.parameters {return false}
12184+
if lhs.returnType != rhs.returnType {return false}
1213912185
if lhs.unknownFields != rhs.unknownFields {return false}
1214012186
return true
1214112187
}

Sources/Fuzzilli/Protobuf/operations.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,9 +1196,13 @@ message WasmReassign {
11961196
}
11971197

11981198
message WasmBeginIf {
1199+
repeated WasmILType parameters = 1;
1200+
WasmILType returnType = 2;
11991201
}
12001202

12011203
message WasmBeginElse {
1204+
repeated WasmILType parameters = 1;
1205+
WasmILType returnType = 2;
12021206
}
12031207

12041208
message WasmEndIf {

0 commit comments

Comments
 (0)