Skip to content

Commit b07e692

Browse files
authored
Add a tolerance parameter to check.expect_almost_eq* ops. (#20146)
Signed-off-by: Benoit Jacob <[email protected]>
1 parent 961a823 commit b07e692

File tree

7 files changed

+164
-68
lines changed

7 files changed

+164
-68
lines changed

compiler/src/iree/compiler/Dialect/VM/Conversion/ImportUtils.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ std::optional<SmallVector<Value>> rewriteAttrToOperands(Location loc,
180180
IntegerAttr::get(inputType, APInt(inputType.getIntOrFloatBitWidth(),
181181
intAttr.getValue().getSExtValue())));
182182
return {{constValue}};
183+
} else if (auto floatAttr = llvm::dyn_cast<FloatAttr>(attrValue)) {
184+
auto constValue = builder.create<mlir::arith::ConstantOp>(
185+
loc, inputType, FloatAttr::get(inputType, floatAttr.getValue()));
186+
return {{constValue}};
183187
} else if (auto elementsAttr =
184188
llvm::dyn_cast<DenseIntElementsAttr>(attrValue)) {
185189
SmallVector<Value> elementValues;

compiler/src/iree/compiler/Modules/Check/IR/CheckOps.cpp

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,73 @@
1414
namespace mlir::iree_compiler::IREE::Check {
1515

1616
namespace {
17-
template <typename SrcOp, typename DstOp>
18-
struct ExpandAttributeToConst : public OpRewritePattern<SrcOp> {
19-
using OpRewritePattern<SrcOp>::OpRewritePattern;
20-
LogicalResult matchAndRewrite(SrcOp op,
17+
// Rewrites expect_eq_const -> expect_eq
18+
struct ExpectEqConstOpToExpectEqOp : public OpRewritePattern<ExpectEqConstOp> {
19+
using OpRewritePattern<ExpectEqConstOp>::OpRewritePattern;
20+
LogicalResult matchAndRewrite(ExpectEqConstOp op,
2121
PatternRewriter &rewriter) const override {
2222
auto rhs = rewriter.create<arith::ConstantOp>(op.getLoc(), op.getValue());
23-
rewriter.replaceOpWithNewOp<DstOp>(op, op.getDevice(), op.getLhs(), rhs);
23+
rewriter.replaceOpWithNewOp<ExpectEqOp>(op, op.getDevice(), op.getLhs(),
24+
rhs);
2425
return success();
2526
}
2627
};
28+
29+
// Rewrites expect_almost_eq_const -> expect_almost_eq
30+
struct ExpectAlmostEqConstOpToExpectAlmostEqOp
31+
: public OpRewritePattern<ExpectAlmostEqConstOp> {
32+
using OpRewritePattern<ExpectAlmostEqConstOp>::OpRewritePattern;
33+
LogicalResult matchAndRewrite(ExpectAlmostEqConstOp op,
34+
PatternRewriter &rewriter) const override {
35+
auto rhs = rewriter.create<arith::ConstantOp>(op.getLoc(), op.getValue());
36+
rewriter.replaceOpWithNewOp<ExpectAlmostEqOp>(
37+
op, op.getDevice(), op.getLhs(), rhs, op.getToleranceAttr());
38+
return success();
39+
}
40+
};
41+
42+
static constexpr char kToleranceKeyword[] = "tolerance";
43+
static constexpr float kToleranceDefaultValue = 1e-4f;
44+
45+
static ParseResult parseOptionalFloatTolerance(OpAsmParser &parser,
46+
FloatAttr &tolerance) {
47+
float toleranceValue = kToleranceDefaultValue;
48+
if (succeeded(parser.parseOptionalComma())) {
49+
if (failed(parser.parseKeyword(kToleranceKeyword))) {
50+
return parser.emitError(parser.getCurrentLocation(),
51+
llvm::Twine("Expected keyword: ") +
52+
kToleranceKeyword);
53+
}
54+
llvm::APFloat parsedTolerance(APFloat::IEEEsingle());
55+
if (failed(parser.parseFloat(parsedTolerance.getSemantics(),
56+
parsedTolerance))) {
57+
return parser.emitError(parser.getCurrentLocation(),
58+
"Failed to parse optional float tolerance.");
59+
}
60+
toleranceValue = parsedTolerance.convertToFloat();
61+
}
62+
tolerance = parser.getBuilder().getF32FloatAttr(toleranceValue);
63+
return success();
64+
}
65+
66+
static void printOptionalFloatTolerance(OpAsmPrinter &p, Operation *op,
67+
FloatAttr tolerance) {
68+
float toleranceValue = tolerance.getValue().convertToFloat();
69+
if (toleranceValue != kToleranceDefaultValue) {
70+
p << ", " << kToleranceKeyword << " " << toleranceValue;
71+
}
72+
}
73+
2774
} // namespace
2875

2976
void ExpectEqConstOp::getCanonicalizationPatterns(RewritePatternSet &results,
3077
MLIRContext *context) {
31-
results.insert<ExpandAttributeToConst<ExpectEqConstOp, ExpectEqOp>>(context);
78+
results.insert<ExpectEqConstOpToExpectEqOp>(context);
3279
}
3380

3481
void ExpectAlmostEqConstOp::getCanonicalizationPatterns(
3582
RewritePatternSet &results, MLIRContext *context) {
36-
results
37-
.insert<ExpandAttributeToConst<ExpectAlmostEqConstOp, ExpectAlmostEqOp>>(
38-
context);
83+
results.insert<ExpectAlmostEqConstOpToExpectAlmostEqOp>(context);
3984
}
4085

4186
} // namespace mlir::iree_compiler::IREE::Check

compiler/src/iree/compiler/Modules/Check/IR/CheckOps.td

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,14 @@ def CHECK_ExpectAlmostEqOp :
150150
let arguments = (ins
151151
Optional<HAL_Device>:$device,
152152
AnyTypeOf<[HAL_BufferView, TensorOf<[AnyFloat]>]>:$lhs,
153-
AnyTypeOf<[HAL_BufferView, TensorOf<[AnyFloat]>]>:$rhs
153+
AnyTypeOf<[HAL_BufferView, TensorOf<[AnyFloat]>]>:$rhs,
154+
F32Attr:$tolerance
154155
);
155156

156157
let assemblyFormat = [{
157158
(`` `<` $device^ `>`)?
158-
`` `(` $lhs `,` $rhs `)` attr-dict `:` type($lhs)
159+
`` `(` $lhs `,` $rhs `` custom<OptionalFloatTolerance>($tolerance) `)`
160+
attr-dict `:` type($lhs)
159161
}];
160162
}
161163

@@ -179,14 +181,16 @@ def CHECK_ExpectAlmostEqConstOp :
179181
let arguments = (ins
180182
Optional<HAL_Device>:$device,
181183
TensorOf<[AnyFloat]>:$lhs,
182-
ElementsAttr:$value
184+
ElementsAttr:$value,
185+
F32Attr:$tolerance
183186
);
184187

185188
let hasCanonicalizer = 1;
186189

187190
let assemblyFormat = [{
188191
(`` `<` $device^ `>`)?
189-
`` `(` $lhs `,` $value `)` attr-dict `:` type($lhs)
192+
`` `(` $lhs `,` $value `` custom<OptionalFloatTolerance>($tolerance) `)`
193+
attr-dict `:` type($lhs)
190194
}];
191195
}
192196

compiler/src/iree/compiler/Modules/Check/check.imports.mlir

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ vm.import private optional @expect_eq(
2828
vm.import private optional @expect_almost_eq(
2929
%device : !vm.ref<!hal.device>,
3030
%lhs : !vm.ref<!hal.buffer_view>,
31-
%rhs : !vm.ref<!hal.buffer_view>
31+
%rhs : !vm.ref<!hal.buffer_view>,
32+
%tolerance : f32
3233
)
3334

3435
} // vm.module

compiler/src/iree/compiler/Modules/Check/test/ops.mlir

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@ util.func public @expect_almost_eq(%lhs : !hal.buffer_view, %rhs : !hal.buffer_v
8585

8686
// -----
8787

88+
// CHECK-LABEL: @expect_almost_eq
89+
// CHECK-SAME: %[[LHS:[a-zA-Z0-9$._-]+]]
90+
// CHECK-SAME: %[[RHS:[a-zA-Z0-9$._-]+]]
91+
util.func public @expect_almost_eq(%lhs : !hal.buffer_view, %rhs : !hal.buffer_view) {
92+
// CHECK: check.expect_almost_eq(%[[LHS]], %[[RHS]], tolerance 1.23{{0*}}e+02) : !hal.buffer_view
93+
check.expect_almost_eq(%lhs, %rhs, tolerance 123.0) : !hal.buffer_view
94+
util.return
95+
}
96+
97+
// -----
98+
8899
// CHECK-LABEL: @expect_almost_eq_tensor
89100
// CHECK-SAME: %[[LHS:[a-zA-Z0-9$._-]+]]
90101
// CHECK-SAME: %[[RHS:[a-zA-Z0-9$._-]+]]
@@ -103,3 +114,13 @@ util.func public @expect_almost_eq_const(%lhs : tensor<2x2xf32>) {
103114
check.expect_almost_eq_const(%lhs, dense<1.0> : tensor<2x2xf32>) : tensor<2x2xf32>
104115
util.return
105116
}
117+
118+
// -----
119+
120+
// CHECK-LABEL: @expect_almost_eq_const
121+
// CHECK-SAME: %[[LHS:[a-zA-Z0-9$._-]+]]
122+
util.func public @expect_almost_eq_const(%lhs : tensor<2x2xf32>) {
123+
// CHECK: check.expect_almost_eq_const(%[[LHS]], dense<1.000000e+00> : tensor<2x2xf32>, tolerance 1.23{{0*}}e+02) : tensor<2x2xf32>
124+
check.expect_almost_eq_const(%lhs, dense<1.0> : tensor<2x2xf32>, tolerance 123.0) : tensor<2x2xf32>
125+
util.return
126+
}

runtime/src/iree/modules/check/check_test.cc

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -182,30 +182,37 @@ class CheckTest : public ::testing::Test {
182182
/*outputs=*/nullptr, iree_allocator_system());
183183
}
184184

185-
iree_status_t InvokeValue(const char* function_name,
186-
std::vector<iree_vm_value_t> args) {
185+
iree_status_t Invoke(const char* function_name,
186+
std::vector<vm::ref<iree_hal_buffer_view_t>> buffer_args,
187+
std::vector<iree_vm_value_t> value_args) {
187188
IREE_RETURN_IF_ERROR(
188-
iree_vm_list_create(iree_vm_make_undefined_type_def(), args.size(),
189+
iree_vm_list_create(iree_vm_make_undefined_type_def(),
190+
buffer_args.size() + value_args.size(),
189191
iree_allocator_system(), &inputs_));
190-
for (auto& arg : args) {
192+
if (!buffer_args.empty()) {
193+
iree_vm_ref_t device_ref = iree_hal_device_retain_ref(device_);
194+
IREE_RETURN_IF_ERROR(
195+
iree_vm_list_push_ref_move(inputs_.get(), &device_ref));
196+
for (auto& arg : buffer_args) {
197+
iree_vm_ref_t arg_ref = iree_hal_buffer_view_retain_ref(arg.get());
198+
IREE_RETURN_IF_ERROR(
199+
iree_vm_list_push_ref_move(inputs_.get(), &arg_ref));
200+
}
201+
}
202+
for (auto& arg : value_args) {
191203
IREE_RETURN_IF_ERROR(iree_vm_list_push_value(inputs_.get(), &arg));
192204
}
193205
return Invoke(function_name);
194206
}
195207

208+
iree_status_t InvokeValue(const char* function_name,
209+
std::vector<iree_vm_value_t> args) {
210+
return Invoke(function_name, /*buffer_args=*/{}, args);
211+
}
212+
196213
iree_status_t Invoke(const char* function_name,
197214
std::vector<vm::ref<iree_hal_buffer_view_t>> args) {
198-
IREE_RETURN_IF_ERROR(
199-
iree_vm_list_create(iree_vm_make_undefined_type_def(), args.size(),
200-
iree_allocator_system(), &inputs_));
201-
iree_vm_ref_t device_ref = iree_hal_device_retain_ref(device_);
202-
IREE_RETURN_IF_ERROR(
203-
iree_vm_list_push_ref_move(inputs_.get(), &device_ref));
204-
for (auto& arg : args) {
205-
iree_vm_ref_t arg_ref = iree_hal_buffer_view_retain_ref(arg.get());
206-
IREE_RETURN_IF_ERROR(iree_vm_list_push_ref_move(inputs_.get(), &arg_ref));
207-
}
208-
return Invoke(function_name);
215+
return Invoke(function_name, args, /*value_args=*/{});
209216
}
210217

211218
static iree_hal_device_t*& device() { return CheckTest::device_; }
@@ -432,8 +439,9 @@ TEST_F(CheckTest, ExpectAlmostEqSameBufferSuccess) {
432439
iree_hal_dim_t shape[] = {1};
433440
ASSERT_NO_FATAL_FAILURE(
434441
CreateFloat32BufferView(contents, shape, &input_buffer_view));
435-
IREE_ASSERT_OK(
436-
Invoke("expect_almost_eq", {input_buffer_view, input_buffer_view}));
442+
IREE_ASSERT_OK(Invoke("expect_almost_eq",
443+
{input_buffer_view, input_buffer_view},
444+
{/*tolerance=*/iree_vm_value_make_f32(0.f)}));
437445
}
438446

439447
TEST_F(CheckTest, ExpectAlmostEqIdenticalBufferSuccess) {
@@ -443,7 +451,8 @@ TEST_F(CheckTest, ExpectAlmostEqIdenticalBufferSuccess) {
443451
iree_hal_dim_t shape[] = {1};
444452
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(contents, shape, &lhs));
445453
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(contents, shape, &rhs));
446-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs}));
454+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
455+
{/*tolerance=*/iree_vm_value_make_f32(0.f)}));
447456
}
448457

449458
TEST_F(CheckTest, ExpectAlmostEqNearIdenticalBufferSuccess) {
@@ -454,7 +463,8 @@ TEST_F(CheckTest, ExpectAlmostEqNearIdenticalBufferSuccess) {
454463
iree_hal_dim_t shape[] = {4};
455464
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(lhs_contents, shape, &lhs));
456465
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(rhs_contents, shape, &rhs));
457-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs}));
466+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
467+
{/*tolerance=*/iree_vm_value_make_f32(1.e-4f)}));
458468
}
459469

460470
TEST_F(CheckTest, ExpectAlmostEqIdentical3DBufferSuccess) {
@@ -464,7 +474,8 @@ TEST_F(CheckTest, ExpectAlmostEqIdentical3DBufferSuccess) {
464474
iree_hal_dim_t shape[] = {2, 2, 2};
465475
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(contents, shape, &lhs));
466476
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(contents, shape, &rhs));
467-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs}));
477+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
478+
{/*tolerance=*/iree_vm_value_make_f32(0.f)}));
468479
}
469480

470481
TEST_F(CheckTest, ExpectAlmostEqDifferentShapeFailure) {
@@ -476,7 +487,8 @@ TEST_F(CheckTest, ExpectAlmostEqDifferentShapeFailure) {
476487
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(contents, lhs_shape, &lhs));
477488
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(contents, rhs_shape, &rhs));
478489
EXPECT_NONFATAL_FAILURE(
479-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs})),
490+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
491+
{/*tolerance=*/iree_vm_value_make_f32(0.f)})),
480492
"Shapes do not match");
481493
}
482494

@@ -492,7 +504,8 @@ TEST_F(CheckTest, ExpectAlmostEqSmallerLhsElementCountFailure) {
492504
ASSERT_NO_FATAL_FAILURE(
493505
CreateFloat32BufferView(bigger_contents, bigger_shape, &bigger));
494506
EXPECT_NONFATAL_FAILURE(
495-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {smaller, bigger})),
507+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {smaller, bigger},
508+
{/*tolerance=*/iree_vm_value_make_f32(0.f)})),
496509
"Shapes do not match");
497510
}
498511

@@ -508,7 +521,8 @@ TEST_F(CheckTest, ExpectAlmostEqSmallerRhsElementCountFailure) {
508521
ASSERT_NO_FATAL_FAILURE(
509522
CreateFloat32BufferView(bigger_contents, bigger_shape, &bigger));
510523
EXPECT_NONFATAL_FAILURE(
511-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {bigger, smaller})),
524+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {bigger, smaller},
525+
{/*tolerance=*/iree_vm_value_make_f32(0.f)})),
512526
"Shapes do not match");
513527
}
514528

@@ -521,7 +535,8 @@ TEST_F(CheckTest, ExpectAlmostEqDifferentElementTypeFailure) {
521535
ASSERT_NO_FATAL_FAILURE(CreateFloat64BufferView(lhs_contents, shape, &lhs));
522536
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(rhs_contents, shape, &rhs));
523537
EXPECT_NONFATAL_FAILURE(
524-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs})),
538+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
539+
{/*tolerance=*/iree_vm_value_make_f32(0.f)})),
525540
"Element types do not match");
526541
}
527542

@@ -534,7 +549,8 @@ TEST_F(CheckTest, ExpectAlmostEqDifferentContentsFailure) {
534549
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(lhs_contents, shape, &lhs));
535550
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(rhs_contents, shape, &rhs));
536551
EXPECT_NONFATAL_FAILURE(
537-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs})),
552+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
553+
{/*tolerance=*/iree_vm_value_make_f32(0.1f)})),
538554
"Contents does not match");
539555
}
540556

@@ -552,7 +568,8 @@ TEST_F(CheckTest, ExpectAlmostEqDifferentEverythingFullMessageFailure) {
552568
// Note no comment on contents. Cannot compare different shapes and element
553569
// types.
554570
EXPECT_NONFATAL_FAILURE(
555-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs})),
571+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
572+
{/*tolerance=*/iree_vm_value_make_f32(0.f)})),
556573
"Expected near equality of these values. Element types do not match."
557574
" Shapes do not match.\n"
558575
" lhs:\n"
@@ -570,8 +587,10 @@ TEST_F(CheckTest, ExpectAlmostEqDifferentContents3DFullMessageFailure) {
570587
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(lhs_contents, shape, &lhs));
571588
ASSERT_NO_FATAL_FAILURE(CreateFloat32BufferView(rhs_contents, shape, &rhs));
572589
EXPECT_NONFATAL_FAILURE(
573-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs})),
574-
"Expected near equality of these values. Contents does not match.\n"
590+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
591+
{/*tolerance=*/iree_vm_value_make_f32(0.1f)})),
592+
"Expected near equality of these values. Contents does not match to "
593+
"tolerance=0.1.\n"
575594
" lhs:\n"
576595
" 2x2x2xf32=[[1 2][3 4]][[5 6][7 8]]\n"
577596
" rhs:\n"
@@ -585,22 +604,24 @@ TEST_F(CheckTest, ExpectAlmostEqIdenticalBufferF16Success) {
585604
iree_hal_dim_t shape[] = {1};
586605
ASSERT_NO_FATAL_FAILURE(CreateFloat16BufferView(contents, shape, &lhs));
587606
ASSERT_NO_FATAL_FAILURE(CreateFloat16BufferView(contents, shape, &rhs));
588-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs}));
607+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
608+
{/*tolerance=*/iree_vm_value_make_f32(0.f)}));
589609
}
590610

591611
TEST_F(CheckTest, ExpectAlmostEqNearIdenticalBufferF16Success) {
592612
vm::ref<iree_hal_buffer_view_t> lhs;
593613
vm::ref<iree_hal_buffer_view_t> rhs;
594614
uint16_t lhs_contents[] = {
595-
iree_math_f32_to_f16(1.0f), iree_math_f32_to_f16(1.99999f),
596-
iree_math_f32_to_f16(0.00001f), iree_math_f32_to_f16(4.0f)};
615+
iree_math_f32_to_f16(1.0f), iree_math_f32_to_f16(1.999f),
616+
iree_math_f32_to_f16(0.001f), iree_math_f32_to_f16(4.0f)};
597617
uint16_t rhs_contents[] = {
598-
iree_math_f32_to_f16(1.00001f), iree_math_f32_to_f16(2.0f),
618+
iree_math_f32_to_f16(1.001f), iree_math_f32_to_f16(2.0f),
599619
iree_math_f32_to_f16(0.0f), iree_math_f32_to_f16(4.0f)};
600620
iree_hal_dim_t shape[] = {4};
601621
ASSERT_NO_FATAL_FAILURE(CreateFloat16BufferView(lhs_contents, shape, &lhs));
602622
ASSERT_NO_FATAL_FAILURE(CreateFloat16BufferView(rhs_contents, shape, &rhs));
603-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs}));
623+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
624+
{/*tolerance=*/iree_vm_value_make_f32(0.01f)}));
604625
}
605626

606627
TEST_F(CheckTest, ExpectAlmostEqDifferentContentsF16Failure) {
@@ -612,7 +633,8 @@ TEST_F(CheckTest, ExpectAlmostEqDifferentContentsF16Failure) {
612633
ASSERT_NO_FATAL_FAILURE(CreateFloat16BufferView(lhs_contents, shape, &lhs));
613634
ASSERT_NO_FATAL_FAILURE(CreateFloat16BufferView(rhs_contents, shape, &rhs));
614635
EXPECT_NONFATAL_FAILURE(
615-
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs})),
636+
IREE_ASSERT_OK(Invoke("expect_almost_eq", {lhs, rhs},
637+
{/*tolerance=*/iree_vm_value_make_f32(0.1f)})),
616638
"Contents does not match");
617639
}
618640
} // namespace

0 commit comments

Comments
 (0)