Skip to content

Commit a949136

Browse files
committed
[WebAssembly] Use 'any' type in more cases in AsmTypeCheck
Now that we support 'any' type in the value stack in the checker, this uses it in more places. When an instruction pops multiple values, rather than popping in one by one and generating multiple error messages, it adds them to a vector and pops them at once. When the type to be popped is not clear, it pops 'any', at least makes sure there are correct number of values on the stack. So for example, in case of `table.fill`, which expects `[i32 t i32]` (where t is the type of the elements in the table), it pops them at once, generating an error message like ```console error: type mismatch, expected [i32, externref, i32] but got [...] ``` In case the table is invalid so we don't know the type, it tries to pop an 'any' instead, popping whatever value there is: ```console error: type mismatch, expected [i32, any, i32] but got [...] ``` Checks done on other instructions based on the register info are already popping and pushing types in vectors, after llvm#110094: https://github.com/llvm/llvm-project/blob/a52251675f001115b225f57362d37e92b7355ef9/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp#L515-L536 This also pushes 'any' in case the type to push is unclear. For example, `local/global.set` pushes a value of the type specified in the local or global, but in case that local or global is invalid, we push 'any' instead, which will match with whatever type. The objective of all these is not to make one instruction's error propragate continuously into subsequent instructions. This also matches Wabt's behavior. This also renames `checkAndPopTypes` to just `popTypes`, to be consistent with a single-element version `popType`. `popType(s)` also does type checks.
1 parent d9853a8 commit a949136

File tree

3 files changed

+99
-53
lines changed

3 files changed

+99
-53
lines changed

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
2020
#include "TargetInfo/WebAssemblyTargetInfo.h"
2121
#include "WebAssembly.h"
22+
#include "llvm/BinaryFormat/Wasm.h"
2223
#include "llvm/MC/MCContext.h"
2324
#include "llvm/MC/MCExpr.h"
2425
#include "llvm/MC/MCInst.h"
@@ -159,15 +160,15 @@ bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
159160
getTypesString(Stack, StackStartPos));
160161
}
161162

162-
bool WebAssemblyAsmTypeCheck::checkAndPopTypes(SMLoc ErrorLoc,
163-
ArrayRef<wasm::ValType> ValTypes,
164-
bool ExactMatch) {
165-
return checkAndPopTypes(ErrorLoc, valTypeToStackType(ValTypes), ExactMatch);
163+
bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
164+
ArrayRef<wasm::ValType> ValTypes,
165+
bool ExactMatch) {
166+
return popTypes(ErrorLoc, valTypeToStackType(ValTypes), ExactMatch);
166167
}
167168

168-
bool WebAssemblyAsmTypeCheck::checkAndPopTypes(SMLoc ErrorLoc,
169-
ArrayRef<StackType> Types,
170-
bool ExactMatch) {
169+
bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
170+
ArrayRef<StackType> Types,
171+
bool ExactMatch) {
171172
bool Error = checkTypes(ErrorLoc, Types, ExactMatch);
172173
auto NumPops = std::min(Stack.size(), Types.size());
173174
for (size_t I = 0, E = NumPops; I != E; I++)
@@ -176,7 +177,7 @@ bool WebAssemblyAsmTypeCheck::checkAndPopTypes(SMLoc ErrorLoc,
176177
}
177178

178179
bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, StackType Type) {
179-
return checkAndPopTypes(ErrorLoc, {Type}, false);
180+
return popTypes(ErrorLoc, {Type});
180181
}
181182

182183
bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
@@ -207,7 +208,7 @@ bool WebAssemblyAsmTypeCheck::checkBr(SMLoc ErrorLoc, size_t Level) {
207208
StringRef("br: invalid depth ") + std::to_string(Level));
208209
const SmallVector<wasm::ValType, 4> &Expected =
209210
BrStack[BrStack.size() - Level - 1];
210-
return checkTypes(ErrorLoc, Expected, false);
211+
return checkTypes(ErrorLoc, Expected);
211212
return false;
212213
}
213214

@@ -216,13 +217,13 @@ bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc, bool PopVals) {
216217
BrStack.pop_back();
217218

218219
if (PopVals)
219-
return checkAndPopTypes(ErrorLoc, LastSig.Returns, false);
220-
return checkTypes(ErrorLoc, LastSig.Returns, false);
220+
return popTypes(ErrorLoc, LastSig.Returns);
221+
return checkTypes(ErrorLoc, LastSig.Returns);
221222
}
222223

223224
bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
224225
const wasm::WasmSignature &Sig) {
225-
bool Error = checkAndPopTypes(ErrorLoc, Sig.Params, false);
226+
bool Error = popTypes(ErrorLoc, Sig.Params);
226227
pushTypes(Sig.Returns);
227228
return Error;
228229
}
@@ -309,7 +310,7 @@ bool WebAssemblyAsmTypeCheck::getSignature(SMLoc ErrorLoc,
309310
}
310311

311312
bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc, bool ExactMatch) {
312-
bool Error = checkAndPopTypes(ErrorLoc, ReturnTypes, ExactMatch);
313+
bool Error = popTypes(ErrorLoc, ReturnTypes, ExactMatch);
313314
Unreachable = true;
314315
return Error;
315316
}
@@ -326,12 +327,14 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
326327
pushType(Type);
327328
return false;
328329
}
330+
pushType(Any{});
329331
return true;
330332
}
331333

332334
if (Name == "local.set") {
333335
if (!getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
334336
return popType(ErrorLoc, Type);
337+
popType(ErrorLoc, Any{});
335338
return true;
336339
}
337340

@@ -341,6 +344,8 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
341344
pushType(Type);
342345
return Error;
343346
}
347+
popType(ErrorLoc, Any{});
348+
pushType(Any{});
344349
return true;
345350
}
346351

@@ -349,12 +354,14 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
349354
pushType(Type);
350355
return false;
351356
}
357+
pushType(Any{});
352358
return true;
353359
}
354360

355361
if (Name == "global.set") {
356362
if (!getGlobal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
357363
return popType(ErrorLoc, Type);
364+
popType(ErrorLoc, Any{});
358365
return true;
359366
}
360367

@@ -364,16 +371,21 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
364371
pushType(Type);
365372
return Error;
366373
}
374+
pushType(Any{});
367375
return true;
368376
}
369377

370378
if (Name == "table.set") {
371379
bool Error = false;
372-
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
373-
Error |= popType(ErrorLoc, Type);
374-
else
380+
SmallVector<StackType, 2> PopTypes;
381+
PopTypes.push_back(wasm::ValType::I32);
382+
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
383+
PopTypes.push_back(Type);
384+
} else {
375385
Error = true;
376-
Error |= popType(ErrorLoc, wasm::ValType::I32);
386+
PopTypes.push_back(Any{});
387+
}
388+
Error |= popTypes(ErrorLoc, PopTypes);
377389
return Error;
378390
}
379391

@@ -384,22 +396,32 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
384396
}
385397

386398
if (Name == "table.grow") {
387-
bool Error = popType(ErrorLoc, wasm::ValType::I32);
388-
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
389-
Error |= popType(ErrorLoc, Type);
390-
else
399+
bool Error = false;
400+
SmallVector<StackType, 2> PopTypes;
401+
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
402+
PopTypes.push_back(Type);
403+
} else {
391404
Error = true;
405+
PopTypes.push_back(Any{});
406+
}
407+
PopTypes.push_back(wasm::ValType::I32);
408+
Error |= popTypes(ErrorLoc, PopTypes);
392409
pushType(wasm::ValType::I32);
393410
return Error;
394411
}
395412

396413
if (Name == "table.fill") {
397-
bool Error = popType(ErrorLoc, wasm::ValType::I32);
398-
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
399-
Error |= popType(ErrorLoc, Type);
400-
else
414+
bool Error = false;
415+
SmallVector<StackType, 2> PopTypes;
416+
PopTypes.push_back(wasm::ValType::I32);
417+
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
418+
PopTypes.push_back(Type);
419+
} else {
401420
Error = true;
402-
Error |= popType(ErrorLoc, wasm::ValType::I32);
421+
PopTypes.push_back(Any{});
422+
}
423+
PopTypes.push_back(wasm::ValType::I32);
424+
Error |= popTypes(ErrorLoc, PopTypes);
403425
return Error;
404426
}
405427

@@ -525,7 +547,7 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
525547
if (Op.OperandType == MCOI::OPERAND_REGISTER)
526548
PopTypes.push_back(WebAssembly::regClassToValType(Op.RegClass));
527549
}
528-
bool Error = checkAndPopTypes(ErrorLoc, PopTypes, false);
550+
bool Error = popTypes(ErrorLoc, PopTypes);
529551
SmallVector<wasm::ValType, 4> PushTypes;
530552
// Now push all the defs onto the stack.
531553
for (unsigned I = 0; I < II.getNumDefs(); I++) {

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,21 @@ class WebAssemblyAsmTypeCheck final {
4040
bool Unreachable = false;
4141
bool Is64;
4242

43+
// checkTypes checks 'Types' against the value stack. popTypes checks 'Types'
44+
// against the value stack and also pops them.
45+
//
4346
// If ExactMatch is true, 'Types' will be compared against not only the top of
4447
// the value stack but the whole remaining value stack
4548
// (TODO: This should be the whole remaining value stack "at the the current
4649
// block level", which has not been implemented yet)
4750
bool checkTypes(SMLoc ErrorLoc, ArrayRef<wasm::ValType> Types,
48-
bool ExactMatch);
49-
bool checkTypes(SMLoc ErrorLoc, ArrayRef<StackType> Types, bool ExactMatch);
50-
bool checkAndPopTypes(SMLoc ErrorLoc, ArrayRef<wasm::ValType> Types,
51-
bool ExactMatch);
52-
bool checkAndPopTypes(SMLoc ErrorLoc, ArrayRef<StackType> Types,
53-
bool ExactMatch);
51+
bool ExactMatch = false);
52+
bool checkTypes(SMLoc ErrorLoc, ArrayRef<StackType> Types,
53+
bool ExactMatch = false);
54+
bool popTypes(SMLoc ErrorLoc, ArrayRef<wasm::ValType> Types,
55+
bool ExactMatch = false);
56+
bool popTypes(SMLoc ErrorLoc, ArrayRef<StackType> Types,
57+
bool ExactMatch = false);
5458
bool popType(SMLoc ErrorLoc, StackType Type);
5559
bool popRefType(SMLoc ErrorLoc);
5660
bool popAnyType(SMLoc ErrorLoc);

llvm/test/MC/WebAssembly/type-checker-errors.s

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -139,31 +139,30 @@ table_set_missing_tabletype:
139139

140140
table_set_empty_stack_while_popping_1:
141141
.functype table_set_empty_stack_while_popping_1 () -> ()
142-
# CHECK: :[[@LINE+2]]:3: error: type mismatch, expected [externref] but got []
143-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
142+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref] but got []
144143
table.set valid_table
145144
end_function
146145

147146
table_set_empty_stack_while_popping_2:
148147
.functype table_set_empty_stack_while_popping_2 (externref) -> ()
149148
local.get 0
150-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
149+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref] but got [externref]
151150
table.set valid_table
152151
end_function
153152

154153
table_set_type_mismatch_1:
155154
.functype table_set_type_mismatch_1 () -> ()
156155
i32.const 0
157156
ref.null_func
158-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [externref] but got [funcref]
157+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref] but got [i32, funcref]
159158
table.set valid_table
160159
end_function
161160

162161
table_set_type_mismatch_2:
163162
.functype table_set_type_mismatch_2 () -> ()
164163
f32.const 1.0
165164
ref.null_extern
166-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got [f32]
165+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref] but got [f32, externref]
167166
table.set valid_table
168167
end_function
169168

@@ -187,25 +186,22 @@ table_fill_missing_tabletype:
187186

188187
table_fill_empty_stack_while_popping_1:
189188
.functype table_fill_empty_stack_while_popping_1 () -> ()
190-
# CHECK: :[[@LINE+3]]:3: error: type mismatch, expected [i32] but got []
191-
# CHECK: :[[@LINE+2]]:3: error: type mismatch, expected [externref] but got []
192-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
189+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got []
193190
table.fill valid_table
194191
end_function
195192

196193
table_fill_empty_stack_while_popping_2:
197194
.functype table_fill_empty_stack_while_popping_2 (i32) -> ()
198195
local.get 0
199-
# CHECK: :[[@LINE+2]]:3: error: type mismatch, expected [externref] but got []
200-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
196+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [i32]
201197
table.fill valid_table
202198
end_function
203199

204200
table_fill_empty_stack_while_popping_3:
205201
.functype table_fill_empty_stack_while_popping_3 (i32, externref) -> ()
206202
local.get 1
207203
local.get 0
208-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
204+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [externref, i32]
209205
table.fill valid_table
210206
end_function
211207

@@ -214,7 +210,7 @@ table_fill_type_mismatch_1:
214210
i32.const 0
215211
ref.null_extern
216212
ref.null_func
217-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got [funcref]
213+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [i32, externref, funcref]
218214
table.fill valid_table
219215
end_function
220216

@@ -223,7 +219,7 @@ table_fill_type_mismatch_2:
223219
i32.const 0
224220
ref.null_func
225221
i32.const 1
226-
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref] but got [funcref]
222+
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [i32, funcref, i32]
227223
table.fill valid_table
228224
end_function
229225

@@ -232,7 +228,7 @@ table_fill_type_mismatch_3:
232228
f32.const 2.0
233229
ref.null_extern
234230
i32.const 1
235-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got [f32]
231+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [f32, externref, i32]
236232
table.fill valid_table
237233
end_function
238234

@@ -241,7 +237,7 @@ table_fill_type_mismatch_4:
241237
i32.const 1
242238
ref.null_exn
243239
i32.const 1
244-
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref] but got [exnref]
240+
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [i32, exnref, i32]
245241
table.fill valid_table
246242
end_function
247243

@@ -256,15 +252,15 @@ table_grow_non_exist_table:
256252
table_grow_type_mismatch_1:
257253
.functype table_grow_type_mismatch_1 (externref, i32) -> (i32)
258254
local.get 1
259-
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref] but got []
255+
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref, i32] but got [i32]
260256
table.grow valid_table
261257
end_function
262258

263259
table_grow_type_mismatch_2:
264260
.functype table_grow_type_mismatch_2 (externref, i32) -> (i32)
265261
local.get 0
266262
local.get 0
267-
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [i32] but got [externref]
263+
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref, i32] but got [externref, externref]
268264
table.grow valid_table
269265
end_function
270266

@@ -883,9 +879,7 @@ multiple_errors_in_function:
883879
# CHECK: :[[@LINE+1]]:13: error: expected expression operand
884880
table.get 1
885881

886-
# CHECK: :[[@LINE+3]]:3: error: type mismatch, expected [i32] but got []
887-
# CHECK: :[[@LINE+2]]:3: error: type mismatch, expected [externref] but got []
888-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
882+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [any]
889883
table.fill valid_table
890884

891885
f32.const 0.0
@@ -905,3 +899,29 @@ call_with_multi_param_and_return:
905899
call take_and_return_multi
906900
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got [i32, i64, f32, f64]
907901
end_function
902+
903+
.functype callee (f32, i32) -> ()
904+
905+
any_value_on_stack:
906+
.functype any_value_on_stack () -> ()
907+
# This local does not exist so it should error out, but it should put an 'any'
908+
# value on the stack so 'call callee' should not error out again
909+
# CHECK: :[[@LINE+1]]:13: error: no local type specified for index 0
910+
local.get 0
911+
i32.const 0
912+
# CHECK-NOT: :[[@LINE+1]]:3: error: type mismatch
913+
call callee
914+
915+
# But this time 'call callee' should error out
916+
i32.const 0
917+
# CHECK: :[[@LINE+1]]:13: error: no local type specified for index 0
918+
local.get 0
919+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [f32, i32] but got [i32, any]
920+
call callee
921+
922+
# CHECK: :[[@LINE+2]]:13: error: no local type specified for index 0
923+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [any] but got []
924+
local.set 0
925+
drop
926+
927+
end_function

0 commit comments

Comments
 (0)