[EmitC] Fix Windows builds (#18546)
`_alloca(0)` returns NULL on the Windows runner, so we make sure to pad
stack allocations to at least one byte.
Fixes #18428
---------
Signed-off-by: Simon Camphausen <simon.camphausen@iml.fraunhofer.de>
diff --git a/build_tools/cmake/ctest_all.sh b/build_tools/cmake/ctest_all.sh
index 176e9a3..cdb930c 100755
--- a/build_tools/cmake/ctest_all.sh
+++ b/build_tools/cmake/ctest_all.sh
@@ -124,10 +124,6 @@
# TODO(#11070): Fix argument/result signature mismatch
"iree/tests/e2e/tosa_ops/check_vmvx_local-sync_microkernels_fully_connected.mlir"
"iree/tests/e2e/tosa_ops/check_vmvx_local-sync_microkernels_matmul.mlir"
- # TODO(#18428): Fix these tests failing on GitHub-hosted Windows runners
- "iree/tests/e2e/stablehlo_models/mnist_fake_weights_llvm_cpu_static_c_test"
- "iree/tests/e2e/stablehlo_models/simple_mul_llvm_cpu_static_c_test"
- "iree/samples/static_library/static_library_demo_c_test"
)
elif [[ "${OSTYPE}" =~ ^darwin ]]; then
excluded_tests+=(
diff --git a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
index 24fb391..1967a4c 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
+++ b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
@@ -1965,6 +1965,11 @@
}
private:
+ struct MaybeZeroValue {
+ Value value;
+ bool isZero;
+ };
+
LogicalResult createVariadicImportShims(IREE::VM::ImportOp &importOp,
OpBuilder &builder) const {
SetVector<const void *> arities;
@@ -2080,23 +2085,18 @@
builder.setInsertionPointToStart(block);
- auto argumentSize = buildSizeExpression(
+ MaybeZeroValue argumentSize = buildSizeExpression(
flattenInputTypes(importOp, segmentSizes, builder), builder, loc);
- auto resultSize =
+ MaybeZeroValue resultSize =
buildSizeExpression(importOp.getResultTypes(), builder, loc);
- if (failed(argumentSize) || failed(resultSize)) {
- return importOp.emitError()
- << "Failed to build size expressions for call struct";
- }
-
const int importArgIndex = 1;
const BlockArgument importArg = newFuncOp.getArgument(importArgIndex);
auto importArgLValue = emitc_builders::asLValue(builder, loc, importArg);
failIfImportUnresolved(builder, loc, importArgLValue);
- auto call = buildIreeVmFunctionCallStruct(
- importArg, argumentSize.value(), resultSize.value(), builder, loc);
+ auto call = buildIreeVmFunctionCallStruct(importArg, argumentSize,
+ resultSize, builder, loc);
if (failed(call)) {
return importOp.emitError() << "failed to create call struct";
@@ -2158,8 +2158,8 @@
return {result};
}
- FailureOr<Value> buildSizeExpression(ArrayRef<Type> types, OpBuilder &builder,
- Location loc) const {
+ MaybeZeroValue buildSizeExpression(ArrayRef<Type> types, OpBuilder &builder,
+ Location loc) const {
auto ctx = builder.getContext();
Type hostSizeType = emitc::OpaqueType::get(ctx, "iree_host_size_t");
@@ -2170,7 +2170,7 @@
/*resultType=*/hostSizeType,
/*value=*/emitc::OpaqueAttr::get(ctx, "0"))
.getResult();
-
+ bool isZero = true;
for (Type type : types) {
Type valueType = typeConverter.convertTypeAsNonPointer(type);
Value size =
@@ -2182,14 +2182,15 @@
/*type=*/hostSizeType,
/*operands=*/ArrayRef<Value>{result, size})
.getResult();
+ isZero = false;
}
- return {result};
+ return MaybeZeroValue{result, isZero};
}
FailureOr<TypedValue<emitc::LValueType>>
- buildIreeVmFunctionCallStruct(Value import, Value argumentSize,
- Value resultSize, OpBuilder &builder,
+ buildIreeVmFunctionCallStruct(Value import, MaybeZeroValue argumentSize,
+ MaybeZeroValue resultSize, OpBuilder &builder,
Location loc) const {
auto ctx = builder.getContext();
@@ -2212,9 +2213,10 @@
return {call};
}
- Value allocateByteSpan(TypedValue<emitc::LValueType> call, Value size,
- StringRef memberName, OpBuilder &builder,
- Location loc) const {
+ Value allocateByteSpan(TypedValue<emitc::LValueType> call,
+ MaybeZeroValue size, StringRef memberName,
+ OpBuilder &builder, Location loc) const {
+
auto ctx = builder.getContext();
// byteSpan = call.<memberName>;
@@ -2226,6 +2228,22 @@
memberName, call)
.getResult();
+ // alloca_(0) returns NULL in some configurations on Windows. Make sure to
+ // allocate at least one byte to get a valid pointer.
+ Value allocaSize;
+ if (size.isZero) {
+ Type hostSizeType = emitc::OpaqueType::get(ctx, "iree_host_size_t");
+
+ allocaSize = builder
+ .create<emitc::ConstantOp>(
+ /*location=*/loc,
+ /*resultType=*/hostSizeType,
+ /*value=*/emitc::OpaqueAttr::get(ctx, "1"))
+ .getResult();
+ } else {
+ allocaSize = size.value;
+ }
+
// void *byteSpan_data_void = iree_alloca(size);
auto byteSpanDataVoid =
builder
@@ -2234,7 +2252,7 @@
/*type=*/
emitc::PointerType::get(emitc::OpaqueType::get(ctx, "void")),
/*callee=*/"iree_alloca",
- /*operands=*/ArrayRef<Value>{size})
+ /*operands=*/ArrayRef<Value>{allocaSize})
.getResult(0);
// uint8_t *byteSpan_data = (uint8_t*)byteSpan_data_void;
@@ -2250,7 +2268,7 @@
emitc_builders::structMemberAssign(builder, loc,
/*memberName=*/"data_length",
/*operand=*/byteSpan,
- /*value=*/size);
+ /*value=*/size.value);
// byteSpan.data = byteSpan_data
emitc_builders::structMemberAssign(builder, loc,
@@ -2259,7 +2277,7 @@
/*value=*/byteSpanData);
// memset(byteSpanData, 0, SIZE);
- emitc_builders::memset(builder, loc, byteSpanData, 0, size);
+ emitc_builders::memset(builder, loc, byteSpanData, 0, allocaSize);
return byteSpan;
}
diff --git a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/test/control_flow_ops.mlir b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/test/control_flow_ops.mlir
index 3beba24..ad01b90 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/test/control_flow_ops.mlir
+++ b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/test/control_flow_ops.mlir
@@ -398,23 +398,27 @@
// Allocate space for the arguments.
// CHECK-NEXT: %[[ARGBYTESPAN_MEMBER:.+]] = "emitc.member"(%[[ARGSTRUCT]]) <{member = "arguments"}> : (!emitc.lvalue<!emitc.opaque<"iree_vm_function_call_t">>) -> !emitc.lvalue<!emitc.opaque<"iree_byte_span_t">>
- // CHECK-NEXT: %[[ARGBYTESPANDATAVOID:.+]] = emitc.call_opaque "iree_alloca"(%[[ARGSIZE]]) : (!emitc.opaque<"iree_host_size_t">) -> !emitc.ptr<!emitc.opaque<"void">>
+ // alloca_(0) can return NULL on Windows. So we always allocate at least one byte
+ // CHECK-NEXT: %[[ARGALLOCASIZE:.+]] = "emitc.constant"() <{value = #emitc.opaque<"1">}> : () -> !emitc.opaque<"iree_host_size_t">
+ // CHECK-NEXT: %[[ARGBYTESPANDATAVOID:.+]] = emitc.call_opaque "iree_alloca"(%[[ARGALLOCASIZE]]) : (!emitc.opaque<"iree_host_size_t">) -> !emitc.ptr<!emitc.opaque<"void">>
// CHECK-NEXT: %[[ARGBYTESPANDATA:.+]] = emitc.cast %[[ARGBYTESPANDATAVOID]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<ui8>
// CHECK-NEXT: %[[ARGSDATALENGTH:.+]] = "emitc.member"(%[[ARGBYTESPAN_MEMBER]]) <{member = "data_length"}> : (!emitc.lvalue<!emitc.opaque<"iree_byte_span_t">>) -> !emitc.lvalue<!emitc.opaque<"iree_host_size_t">>
// CHECK-NEXT: emitc.assign %[[ARGSIZE]] : !emitc.opaque<"iree_host_size_t"> to %[[ARGSDATALENGTH]] : <!emitc.opaque<"iree_host_size_t">>
// CHECK-NEXT: %[[ARGSDATA:.+]] = "emitc.member"(%[[ARGBYTESPAN_MEMBER]]) <{member = "data"}> : (!emitc.lvalue<!emitc.opaque<"iree_byte_span_t">>) -> !emitc.lvalue<!emitc.ptr<ui8>>
// CHECK-NEXT: emitc.assign %[[ARGBYTESPANDATA]] : !emitc.ptr<ui8> to %[[ARGSDATA]] : <!emitc.ptr<ui8>>
- // CHECK-NEXT: emitc.call_opaque "memset"(%[[ARGBYTESPANDATA]], %[[ARGSIZE]]) {args = [0 : index, 0 : ui32, 1 : index]}
+ // CHECK-NEXT: emitc.call_opaque "memset"(%[[ARGBYTESPANDATA]], %[[ARGALLOCASIZE]]) {args = [0 : index, 0 : ui32, 1 : index]}
// Allocate space for the result.
// CHECK-NEXT: %[[RESBYTESPAN_MEMBER:.+]] = "emitc.member"(%[[ARGSTRUCT]]) <{member = "results"}> : (!emitc.lvalue<!emitc.opaque<"iree_vm_function_call_t">>) -> !emitc.lvalue<!emitc.opaque<"iree_byte_span_t">>
- // CHECK-NEXT: %[[RESBYTESPANDATAVOID:.+]] = emitc.call_opaque "iree_alloca"(%[[RESULTSIZE]]) : (!emitc.opaque<"iree_host_size_t">) -> !emitc.ptr<!emitc.opaque<"void">>
+ // alloca_(0) can return NULL on Windows. So we always allocate at least one byte
+ // CHECK-NEXT: %[[RESALLOCASIZE:.+]] = "emitc.constant"() <{value = #emitc.opaque<"1">}> : () -> !emitc.opaque<"iree_host_size_t">
+ // CHECK-NEXT: %[[RESBYTESPANDATAVOID:.+]] = emitc.call_opaque "iree_alloca"(%[[RESALLOCASIZE]]) : (!emitc.opaque<"iree_host_size_t">) -> !emitc.ptr<!emitc.opaque<"void">>
// CHECK-NEXT: %[[RESBYTESPANDATA:.+]] = emitc.cast %[[RESBYTESPANDATAVOID]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<ui8>
// CHECK-NEXT: %[[RESSDATALENGTH:.+]] = "emitc.member"(%[[RESBYTESPAN_MEMBER]]) <{member = "data_length"}> : (!emitc.lvalue<!emitc.opaque<"iree_byte_span_t">>) -> !emitc.lvalue<!emitc.opaque<"iree_host_size_t">>
// CHECK-NEXT: emitc.assign %[[RESULTSIZE]] : !emitc.opaque<"iree_host_size_t"> to %[[RESSDATALENGTH]] : <!emitc.opaque<"iree_host_size_t">>
// CHECK-NEXT: %[[RESSDATA:.+]] = "emitc.member"(%[[RESBYTESPAN_MEMBER]]) <{member = "data"}> : (!emitc.lvalue<!emitc.opaque<"iree_byte_span_t">>) -> !emitc.lvalue<!emitc.ptr<ui8>>
// CHECK-NEXT: emitc.assign %[[RESBYTESPANDATA]] : !emitc.ptr<ui8> to %[[RESSDATA]] : <!emitc.ptr<ui8>>
- // CHECK-NEXT: emitc.call_opaque "memset"(%[[RESBYTESPANDATA]], %[[RESULTSIZE]]) {args = [0 : index, 0 : ui32, 1 : index]}
+ // CHECK-NEXT: emitc.call_opaque "memset"(%[[RESBYTESPANDATA]], %[[RESALLOCASIZE]]) {args = [0 : index, 0 : ui32, 1 : index]}
// Check that we don't pack anything into the argument struct.
// CHECK-NOT: "emitc.member"(%{{.+}}) <{member = "arguments"}>