Detect functions that fail to return a value, without using CFG.

This check now runs at function finalization time, before constant
propagation has occurred; this affected the "DeadIfStatement" test.

Our detection isn't smart enough to realize that a loop will run zero
times, so it treats `for` and `while` loops as always running at least
once. This isn't strictly correct, but it actually mirrors how the CFG
implementation works anyway. The only downside is that we would not flag
code like `for (i=0; i<0; ++i) { return x; }` as an error.

Change-Id: I5e43a6ee3a3993045559f0fb0646d36112543a94
Bug: skia:11377
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/379056
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2021-03-04 16:00:20 -05:00 committed by Skia Commit-Bot
parent daed2592bb
commit b3dcbb12ef
19 changed files with 1734 additions and 31 deletions

View File

@ -397,6 +397,8 @@ sksl_shared_tests = [
"/sksl/shared/ResizeMatrixNonsquare.sksl",
"/sksl/shared/ReturnBadTypeFromMain.sksl",
"/sksl/shared/ReturnColorFromMain.sksl",
"/sksl/shared/ReturnsValueOnEveryPathES2.sksl",
"/sksl/shared/ReturnsValueOnEveryPathES3.sksl",
"/sksl/shared/SampleLocations.vert",
"/sksl/shared/SampleMask.sksl",
"/sksl/shared/ScalarConversionConstructorsES2.sksl",

View File

@ -1,4 +1,98 @@
// Expect 1 error
/*#pragma settings NoControlFlowAnalysis*/
int not_detected() { if (sqrt(1) == 1) return 3; } // function finalizer doesn't analyze all paths
int is_detected() { if (2 > 5) return 3; } // optimizer reduces this to an empty function
// Expect 17 errors
int variable = 0;
uniform half mystery;
int if_only() { if (variable == 1) return 0; }
int return_on_if_but_not_else() { if (variable == 1) return 0; else variable *= 2; }
int return_on_else_but_not_if() { if (variable == 1) variable *= 2; else return 1; }
int for_with_conditional_return() { for (;;) { if (variable == 1) return 0; } }
int for_with_conditional_break() { for (;;) { if (variable == 1) break; return 0; } }
int for_with_conditional_continue() { for (;;) { if (variable == 1) continue; return 0; } }
bool bad_if_else_chain() {
if (mystery == 1)
return true;
else if (mystery == 2)
return false;
else if (mystery == 3)
return true;
else if (mystery == 4)
variable *= 2; // doesn't return
else
return true;
}
bool conditional_inside_do_loop() {
do {
if (mystery == 123) return true;
} while (true);
}
int switch_empty() {
switch (variable) {}
}
int switch_with_no_default() {
switch (variable) {
case 1: return 1;
case 2: return 2;
}
}
int switch_with_break() {
switch (variable) {
case 1: return 1;
case 2: break;
default: return 3;
}
}
int switch_with_continue() {
for (;;) switch (variable) {
case 1: return 1;
case 2: return 2;
default: continue;
}
}
int switch_with_fallthrough_off_bottom() {
switch (variable) {
case 1: return 1;
case 2: return 2;
default:
}
}
int switch_with_conditional_break() {
switch (variable) {
case 1: if (mystery == 123) break; return 1;
case 2: return 2;
default: return 3;
}
}
int switch_with_conditional_continue() {
for (;;) switch (variable) {
case 1: if (mystery == 123) ; else continue; return 1;
case 2: return 2;
default: return 3;
}
}
int switch_with_conditional_if_then_return() {
switch (variable) {
case 1: if (mystery == 123) break; return 1;
default: return 2;
}
}
int switch_with_conditional_break_then_fallthrough() {
switch (variable) {
case 1: if (mystery == 123) {} else break;
default: return 2;
}
}

View File

@ -1,7 +1,7 @@
uniform half4 colorGreen, colorRed;
half4 main() {
bool x = true;
if (x) return colorGreen;
const bool x = true;
if (!x) return colorRed;
if (x) return colorGreen;
}

View File

@ -0,0 +1,47 @@
uniform half4 colorGreen, colorRed;
uniform half unknownInput; // = 1
bool simple() {
return true;
}
bool return_on_both_sides() {
if (unknownInput == 1) return true; else return true;
}
bool for_inside_body() {
for (int x=0; x<=10; ++x) { return true; }
}
bool after_for_body() {
for (int x=0; x<=10; ++x) { simple(); }
return true;
}
bool for_with_double_sided_conditional_return() {
for (int x=0; x<=10; ++x) {
if (unknownInput == 1) return true; else return true;
}
}
bool if_else_chain() {
if (unknownInput == 1)
return true;
else if (unknownInput == 2)
return false;
else if (unknownInput == 3)
return true;
else if (unknownInput == 4)
return false;
else
return true;
}
half4 main() {
return simple() &&
return_on_both_sides() &&
for_inside_body() &&
after_for_body() &&
for_with_double_sided_conditional_return() &&
if_else_chain() ? colorGreen : colorRed;
}

View File

@ -0,0 +1,152 @@
/*#pragma settings NoControlFlowAnalysis*/
uniform half4 colorGreen, colorRed;
uniform half unknownInput; // = 1
bool simple() {
return true;
}
bool return_on_both_sides() {
if (unknownInput == 1) return true; else return true;
}
bool for_inside_body() {
for (int x=0; x<=10; ++x) { return true; }
}
bool after_for_body() {
for (int x=0; x<=10; ++x) { simple(); }
return true;
}
bool for_with_double_sided_conditional_return() {
for (int x=0; x<=10; ++x) {
if (unknownInput == 1) return true; else return true;
}
}
bool if_else_chain() {
if (unknownInput == 1)
return true;
else if (unknownInput == 2)
return false;
else if (unknownInput == 3)
return true;
else if (unknownInput == 4)
return false;
else
return true;
}
bool conditional_inside_while_loop() {
while (unknownInput == 123) {
return true;
}
}
bool inside_do_loop() {
do {
return true;
} while (true);
}
bool inside_while_loop() {
while (true) {
return true;
}
}
bool after_do_loop() {
do {
break;
} while (true);
return true;
}
bool after_while_loop() {
while (true) {
break;
}
return true;
}
bool switch_with_all_returns() {
switch (int(unknownInput)) {
case 1: return true;
case 2: return true;
default: return true;
}
}
bool switch_only_default() {
switch (int(unknownInput)) {
default: return true;
}
}
bool switch_fallthrough() {
switch (int(unknownInput)) {
case 1: return true;
case 2:
default: return true;
}
}
bool switch_fallthrough_twice() {
switch (int(unknownInput)) {
case 1:
case 2:
default: return true;
}
}
bool switch_with_break_in_loop() {
switch (int(unknownInput)) {
case 1: for (int x=0; x<=10; ++x) { break; }
default: return true;
}
}
bool switch_with_continue_in_loop() {
switch (int(unknownInput)) {
case 1: for (int x=0; x<=10; ++x) { continue; }
default: return true;
}
}
bool switch_with_if_that_returns() {
switch (int(unknownInput)) {
case 1: if (unknownInput == 123) return true; else return true;
default: return true;
}
}
bool switch_with_one_sided_if_then_fallthrough() {
switch (int(unknownInput)) {
case 1: if (unknownInput == 123) return true;
default: return true;
}
}
half4 main() {
return simple() &&
return_on_both_sides() &&
for_inside_body() &&
after_for_body() &&
for_with_double_sided_conditional_return() &&
if_else_chain() &&
conditional_inside_while_loop() &&
inside_do_loop() &&
inside_while_loop() &&
after_do_loop() &&
after_while_loop() &&
switch_with_all_returns() &&
switch_only_default() &&
switch_fallthrough() &&
switch_fallthrough_twice() &&
switch_with_break_in_loop() &&
switch_with_continue_in_loop() &&
switch_with_if_that_returns() &&
switch_with_one_sided_if_then_fallthrough() ? colorGreen : colorRed;
}

View File

@ -392,6 +392,143 @@ public:
using INHERITED = ProgramVisitor;
};
class ReturnsOnAllPathsVisitor : public ProgramVisitor {
public:
bool visitExpression(const Expression& expr) override {
// We can avoid processing expressions entirely.
return false;
}
bool visitStatement(const Statement& stmt) override {
switch (stmt.kind()) {
// Returns, breaks, or continues will stop the scan, so only one of these should ever be
// true.
case Statement::Kind::kReturn:
fFoundReturn = true;
return true;
case Statement::Kind::kBreak:
fFoundBreak = true;
return true;
case Statement::Kind::kContinue:
fFoundContinue = true;
return true;
case Statement::Kind::kIf: {
const IfStatement& i = stmt.as<IfStatement>();
ReturnsOnAllPathsVisitor trueVisitor;
ReturnsOnAllPathsVisitor falseVisitor;
trueVisitor.visitStatement(*i.ifTrue());
if (i.ifFalse()) {
falseVisitor.visitStatement(*i.ifFalse());
}
// If either branch leads to a break or continue, we report the entire if as
// containing a break or continue, since we don't know which side will be reached.
fFoundBreak = (trueVisitor.fFoundBreak || falseVisitor.fFoundBreak);
fFoundContinue = (trueVisitor.fFoundContinue || falseVisitor.fFoundContinue);
// On the other hand, we only want to report returns that definitely happen, so we
// require those to be found on both sides.
fFoundReturn = (trueVisitor.fFoundReturn && falseVisitor.fFoundReturn);
return fFoundBreak || fFoundContinue || fFoundReturn;
}
case Statement::Kind::kFor: {
const ForStatement& f = stmt.as<ForStatement>();
// We assume a for/while loop runs for at least one iteration; this isn't strictly
// guaranteed, but it's better to be slightly over-permissive here than to fail on
// reasonable code.
ReturnsOnAllPathsVisitor forVisitor;
forVisitor.visitStatement(*f.statement());
// A for loop that contains a break or continue is safe; it won't exit the entire
// function, just the loop. So we disregard those signals.
fFoundReturn = forVisitor.fFoundReturn;
return fFoundReturn;
}
case Statement::Kind::kDo: {
const DoStatement& d = stmt.as<DoStatement>();
// Do-while blocks are always entered at least once.
ReturnsOnAllPathsVisitor doVisitor;
doVisitor.visitStatement(*d.statement());
// A do-while loop that contains a break or continue is safe; it won't exit the
// entire function, just the loop. So we disregard those signals.
fFoundReturn = doVisitor.fFoundReturn;
return fFoundReturn;
}
case Statement::Kind::kBlock:
// Blocks are definitely entered and don't imply any additional control flow.
// If the block contains a break, continue or return, we want to keep that.
return INHERITED::visitStatement(stmt);
case Statement::Kind::kSwitch: {
// Switches are the most complex control flow we need to deal with; fortunately we
// already have good primitives for dissecting them. We need to verify that:
// - a default case exists, so that every possible input value is covered
// - every switch-case either (a) returns unconditionally, or
// (b) falls through to another case that does
const SwitchStatement& s = stmt.as<SwitchStatement>();
bool foundDefault = false;
bool fellThrough = false;
for (const std::unique_ptr<SwitchCase>& sc : s.cases()) {
// The default case is indicated by a null value. A switch without a default
// case cannot definitively return, as its value might not be in the cases list.
if (!sc->value()) {
foundDefault = true;
}
// Scan this switch-case for any exit (break, continue or return).
ReturnsOnAllPathsVisitor caseVisitor;
caseVisitor.visitStatement(*sc);
// If we found a break or continue, whether conditional or not, this switch case
// can't be called an unconditional return. Switches absorb breaks but not
// continues.
if (caseVisitor.fFoundContinue) {
fFoundContinue = true;
return false;
}
if (caseVisitor.fFoundBreak) {
return false;
}
// We just confirmed that there weren't any breaks or continues. If we didn't
// find an unconditional return either, the switch is considered fallen-through.
// (There might be a conditional return, but that doesn't count.)
fellThrough = !caseVisitor.fFoundReturn;
}
// If we didn't find a default case, or the very last case fell through, this switch
// doesn't meet our criteria.
if (fellThrough || !foundDefault) {
return false;
}
// We scanned the entire switch, found a default case, and every section either fell
// through or contained an unconditional return.
fFoundReturn = true;
return true;
}
case Statement::Kind::kSwitchCase:
// Recurse into the switch-case.
return INHERITED::visitStatement(stmt);
case Statement::Kind::kDiscard:
case Statement::Kind::kExpression:
case Statement::Kind::kInlineMarker:
case Statement::Kind::kNop:
case Statement::Kind::kVarDeclaration:
// None of these statements could contain a return.
break;
}
return false;
}
bool fFoundReturn = false;
bool fFoundBreak = false;
bool fFoundContinue = false;
using INHERITED = ProgramVisitor;
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
@ -906,6 +1043,12 @@ bool Analysis::IsConstantExpression(const Expression& expr) {
return !visitor.visitExpression(expr);
}
bool Analysis::CanExitWithoutReturningValue(const FunctionDefinition& funcDef) {
ReturnsOnAllPathsVisitor visitor;
visitor.visitStatement(*funcDef.body());
return !visitor.fFoundReturn;
}
////////////////////////////////////////////////////////////////////////////////
// ProgramVisitor

View File

@ -48,6 +48,13 @@ struct Analysis {
*/
static bool SwitchCaseContainsUnconditionalExit(Statement& stmt);
/**
* A switch-case "falls through" when it doesn't have an unconditional exit.
*/
static bool SwitchCaseFallsThrough(Statement& stmt) {
return !SwitchCaseContainsUnconditionalExit(stmt);
}
/**
* Finds conditional exits from a switch-case. Returns true if this statement contains a
* conditional that wraps a potential exit from the switch (via continue, break or return).
@ -127,6 +134,9 @@ struct Analysis {
ErrorReporter* errors);
static void ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors);
// Detects functions that fail to return a value on at least one path.
static bool CanExitWithoutReturningValue(const FunctionDefinition& funcDef);
};
/**

View File

@ -931,24 +931,12 @@ void IRGenerator::finalizeFunction(FunctionDefinition& f) {
~Finalizer() override {
SkASSERT(!fBreakableLevel);
SkASSERT(!fContinuableLevel);
if (!fEncounteredReturnValue && this->functionReturnsValue()) {
// It's a non-void function, but it never created a result expression--that is, it
// never returned anything.
fIRGenerator->errorReporter().error(
fFunction->fOffset,
"function '" + fFunction->name() + "' exits without returning a value");
}
}
bool functionReturnsValue() const {
return fFunction->returnType() != *fIRGenerator->fContext.fTypes.fVoid;
}
bool encounteredReturnValue() const {
return fEncounteredReturnValue;
}
bool visitStatement(Statement& stmt) override {
switch (stmt.kind()) {
case Statement::Kind::kReturn: {
@ -968,7 +956,6 @@ void IRGenerator::finalizeFunction(FunctionDefinition& f) {
// Coerce return expression to the function's return type.
returnStmt.setExpression(fIRGenerator->coerce(
std::move(returnStmt.expression()), returnType));
fEncounteredReturnValue = true;
} else {
// Returning something from a function with a void return type.
fIRGenerator->errorReporter().error(returnStmt.fOffset,
@ -1023,12 +1010,17 @@ void IRGenerator::finalizeFunction(FunctionDefinition& f) {
int fBreakableLevel = 0;
// how deeply nested we are in continuable constructs (for, do).
int fContinuableLevel = 0;
// have we found a return statement with a return value?
bool fEncounteredReturnValue = false;
using INHERITED = ProgramWriter;
};
Finalizer(this, &f.declaration()).visitStatement(*f.body());
Finalizer finalizer{this, &f.declaration()};
finalizer.visitStatement(*f.body());
if (finalizer.functionReturnsValue() && Analysis::CanExitWithoutReturningValue(f)) {
this->errorReporter().error(f.fOffset, "function '" + f.declaration().name() +
"' can exit without returning a value");
}
}
void IRGenerator::convertFunction(const ASTNode& f) {

View File

@ -1066,14 +1066,23 @@ DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLFunction, r, ctxInfo) {
}
{
ExpectError error(r, "error: expected function to return 'float'\n"
"error: function 'broken' exits without returning a value\n");
ExpectError error(r, "error: expected function to return 'float'\n");
DSLWriter::ProgramElements().clear();
DSLFunction(kFloat, "broken").define(
Return()
);
}
{
ExpectError error(r, "error: function 'broken' can exit without returning a value\n");
DSLWriter::ProgramElements().clear();
Var x(kFloat, "x");
DSLFunction(kFloat, "broken").define(
Declare(x, 0),
If(x == 1, Return(x))
);
}
{
ExpectError error(r, "error: may not return a value from a void function\n");
DSLWriter::ProgramElements().clear();
@ -1082,14 +1091,12 @@ DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLFunction, r, ctxInfo) {
);
}
/* TODO: detect this case
{
ExpectError error(r, "error: expected function to return 'float'\n");
ExpectError error(r, "error: function 'broken' can exit without returning a value\n");
DSLWriter::ProgramElements().clear();
DSLFunction(kFloat, "broken").define(
);
}
*/
}
DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLIf, r, ctxInfo) {

View File

@ -170,6 +170,7 @@ SKSL_TEST(SkSLOperatorsES2, "shared/OperatorsES2.sksl")
SKSL_TEST(SkSLOutParams, "shared/OutParams.sksl")
SKSL_TEST(SkSLOutParamsTricky, "shared/OutParamsTricky.sksl")
SKSL_TEST(SkSLResizeMatrix, "shared/ResizeMatrix.sksl")
SKSL_TEST(SkSLReturnsValueOnEveryPathES2, "shared/ReturnsValueOnEveryPathES2.sksl")
SKSL_TEST(SkSLScalarConversionConstructorsES2, "shared/ScalarConversionConstructorsES2.sksl")
SKSL_TEST(SkSLStackingVectorCasts, "shared/StackingVectorCasts.sksl")
SKSL_TEST(SkSLStaticIf, "shared/StaticIf.sksl")
@ -213,6 +214,7 @@ SKSL_TEST(SkSLHexUnsigned, "shared/HexUnsigned.sksl")
SKSL_TEST(SkSLMatricesNonsquare, "shared/MatricesNonsquare.sksl")
SKSL_TEST(SkSLOperatorsES3, "shared/OperatorsES3.sksl")
SKSL_TEST(SkSLResizeMatrixNonsquare, "shared/ResizeMatrixNonsquare.sksl")
SKSL_TEST(SkSLReturnsValueOnEveryPathES3, "shared/ReturnsValueOnEveryPathES3.sksl")
SKSL_TEST(SkSLScalarConversionConstructorsES3, "shared/ScalarConversionConstructorsES3.sksl")
SKSL_TEST(SkSLSwizzleByIndex, "shared/SwizzleByIndex.sksl")
SKSL_TEST(SkSLWhileLoopControlFlow, "shared/WhileLoopControlFlow.sksl")

View File

@ -1,4 +1,20 @@
### Compilation failed:
error: 4: function 'is_detected' exits without returning a value
1 error
error: 8: function 'if_only' can exit without returning a value
error: 9: function 'return_on_if_but_not_else' can exit without returning a value
error: 10: function 'return_on_else_but_not_if' can exit without returning a value
error: 12: function 'for_with_conditional_return' can exit without returning a value
error: 13: function 'for_with_conditional_break' can exit without returning a value
error: 14: function 'for_with_conditional_continue' can exit without returning a value
error: 16: function 'bad_if_else_chain' can exit without returning a value
error: 29: function 'conditional_inside_do_loop' can exit without returning a value
error: 35: function 'switch_empty' can exit without returning a value
error: 39: function 'switch_with_no_default' can exit without returning a value
error: 46: function 'switch_with_break' can exit without returning a value
error: 54: function 'switch_with_continue' can exit without returning a value
error: 62: function 'switch_with_fallthrough_off_bottom' can exit without returning a value
error: 70: function 'switch_with_conditional_break' can exit without returning a value
error: 78: function 'switch_with_conditional_continue' can exit without returning a value
error: 86: function 'switch_with_conditional_if_then_return' can exit without returning a value
error: 93: function 'switch_with_conditional_break_then_fallthrough' can exit without returning a value
17 errors

View File

@ -1,4 +1,4 @@
### Compilation failed:
error: 1: function 'n' exits without returning a value
error: 1: function 'n' can exit without returning a value
1 error

View File

@ -1,5 +1,4 @@
### Compilation failed:
error: 1: expected function to return 'int'
error: 1: function 'foo' exits without returning a value
2 errors
1 error

View File

@ -0,0 +1,262 @@
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %_entrypoint "_entrypoint" %sk_FragColor %sk_Clockwise
OpExecutionMode %_entrypoint OriginUpperLeft
OpName %sk_FragColor "sk_FragColor"
OpName %sk_Clockwise "sk_Clockwise"
OpName %_UniformBuffer "_UniformBuffer"
OpMemberName %_UniformBuffer 0 "colorGreen"
OpMemberName %_UniformBuffer 1 "colorRed"
OpMemberName %_UniformBuffer 2 "unknownInput"
OpName %_entrypoint "_entrypoint"
OpName %for_inside_body "for_inside_body"
OpName %x "x"
OpName %after_for_body "after_for_body"
OpName %x_0 "x"
OpName %for_with_double_sided_conditional_return "for_with_double_sided_conditional_return"
OpName %x_1 "x"
OpName %if_else_chain "if_else_chain"
OpName %main "main"
OpName %_0_return_on_both_sides "_0_return_on_both_sides"
OpDecorate %sk_FragColor RelaxedPrecision
OpDecorate %sk_FragColor Location 0
OpDecorate %sk_FragColor Index 0
OpDecorate %sk_Clockwise RelaxedPrecision
OpDecorate %sk_Clockwise BuiltIn FrontFacing
OpMemberDecorate %_UniformBuffer 0 Offset 0
OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision
OpMemberDecorate %_UniformBuffer 1 Offset 16
OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision
OpMemberDecorate %_UniformBuffer 2 Offset 32
OpMemberDecorate %_UniformBuffer 2 RelaxedPrecision
OpDecorate %_UniformBuffer Block
OpDecorate %14 Binding 0
OpDecorate %14 DescriptorSet 0
OpDecorate %63 RelaxedPrecision
OpDecorate %73 RelaxedPrecision
OpDecorate %79 RelaxedPrecision
OpDecorate %87 RelaxedPrecision
OpDecorate %94 RelaxedPrecision
OpDecorate %105 RelaxedPrecision
OpDecorate %110 RelaxedPrecision
OpDecorate %134 RelaxedPrecision
OpDecorate %136 RelaxedPrecision
OpDecorate %137 RelaxedPrecision
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%sk_FragColor = OpVariable %_ptr_Output_v4float Output
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%_UniformBuffer = OpTypeStruct %v4float %v4float %float
%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
%14 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
%void = OpTypeVoid
%19 = OpTypeFunction %void
%22 = OpTypeFunction %bool
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%true = OpConstantTrue %bool
%int_1 = OpConstant %int 1
%_ptr_Uniform_float = OpTypePointer Uniform %float
%int_2 = OpConstant %int 2
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%false = OpConstantFalse %bool
%float_3 = OpConstant %float 3
%float_4 = OpConstant %float 4
%100 = OpTypeFunction %v4float
%_ptr_Function_bool = OpTypePointer Function %bool
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_entrypoint = OpFunction %void None %19
%20 = OpLabel
%21 = OpFunctionCall %v4float %main
OpStore %sk_FragColor %21
OpReturn
OpFunctionEnd
%for_inside_body = OpFunction %bool None %22
%23 = OpLabel
%x = OpVariable %_ptr_Function_int Function
OpStore %x %int_0
OpBranch %28
%28 = OpLabel
OpLoopMerge %32 %31 None
OpBranch %29
%29 = OpLabel
%33 = OpLoad %int %x
%35 = OpSLessThanEqual %bool %33 %int_10
OpBranchConditional %35 %30 %32
%30 = OpLabel
OpReturnValue %true
%31 = OpLabel
%38 = OpLoad %int %x
%39 = OpIAdd %int %38 %int_1
OpStore %x %39
OpBranch %28
%32 = OpLabel
OpUnreachable
OpFunctionEnd
%after_for_body = OpFunction %bool None %22
%40 = OpLabel
%x_0 = OpVariable %_ptr_Function_int Function
OpStore %x_0 %int_0
OpBranch %42
%42 = OpLabel
OpLoopMerge %46 %45 None
OpBranch %43
%43 = OpLabel
%47 = OpLoad %int %x_0
%48 = OpSLessThanEqual %bool %47 %int_10
OpBranchConditional %48 %44 %46
%44 = OpLabel
OpBranch %45
%45 = OpLabel
%49 = OpLoad %int %x_0
%50 = OpIAdd %int %49 %int_1
OpStore %x_0 %50
OpBranch %42
%46 = OpLabel
OpReturnValue %true
OpFunctionEnd
%for_with_double_sided_conditional_return = OpFunction %bool None %22
%51 = OpLabel
%x_1 = OpVariable %_ptr_Function_int Function
OpStore %x_1 %int_0
OpBranch %53
%53 = OpLabel
OpLoopMerge %57 %56 None
OpBranch %54
%54 = OpLabel
%58 = OpLoad %int %x_1
%59 = OpSLessThanEqual %bool %58 %int_10
OpBranchConditional %59 %55 %57
%55 = OpLabel
%60 = OpAccessChain %_ptr_Uniform_float %14 %int_2
%63 = OpLoad %float %60
%65 = OpFOrdEqual %bool %63 %float_1
OpSelectionMerge %68 None
OpBranchConditional %65 %66 %67
%66 = OpLabel
OpReturnValue %true
%67 = OpLabel
OpReturnValue %true
%68 = OpLabel
OpBranch %56
%56 = OpLabel
%69 = OpLoad %int %x_1
%70 = OpIAdd %int %69 %int_1
OpStore %x_1 %70
OpBranch %53
%57 = OpLabel
OpUnreachable
OpFunctionEnd
%if_else_chain = OpFunction %bool None %22
%71 = OpLabel
%72 = OpAccessChain %_ptr_Uniform_float %14 %int_2
%73 = OpLoad %float %72
%74 = OpFOrdEqual %bool %73 %float_1
OpSelectionMerge %77 None
OpBranchConditional %74 %75 %76
%75 = OpLabel
OpReturnValue %true
%76 = OpLabel
%78 = OpAccessChain %_ptr_Uniform_float %14 %int_2
%79 = OpLoad %float %78
%81 = OpFOrdEqual %bool %79 %float_2
OpSelectionMerge %84 None
OpBranchConditional %81 %82 %83
%82 = OpLabel
OpReturnValue %false
%83 = OpLabel
%86 = OpAccessChain %_ptr_Uniform_float %14 %int_2
%87 = OpLoad %float %86
%89 = OpFOrdEqual %bool %87 %float_3
OpSelectionMerge %92 None
OpBranchConditional %89 %90 %91
%90 = OpLabel
OpReturnValue %true
%91 = OpLabel
%93 = OpAccessChain %_ptr_Uniform_float %14 %int_2
%94 = OpLoad %float %93
%96 = OpFOrdEqual %bool %94 %float_4
OpSelectionMerge %99 None
OpBranchConditional %96 %97 %98
%97 = OpLabel
OpReturnValue %false
%98 = OpLabel
OpReturnValue %true
%99 = OpLabel
OpBranch %92
%92 = OpLabel
OpBranch %84
%84 = OpLabel
OpBranch %77
%77 = OpLabel
OpUnreachable
OpFunctionEnd
%main = OpFunction %v4float None %100
%101 = OpLabel
%_0_return_on_both_sides = OpVariable %_ptr_Function_bool Function
%127 = OpVariable %_ptr_Function_v4float Function
%104 = OpAccessChain %_ptr_Uniform_float %14 %int_2
%105 = OpLoad %float %104
%106 = OpFOrdEqual %bool %105 %float_1
OpSelectionMerge %109 None
OpBranchConditional %106 %107 %108
%107 = OpLabel
OpStore %_0_return_on_both_sides %true
OpBranch %109
%108 = OpLabel
OpStore %_0_return_on_both_sides %true
OpBranch %109
%109 = OpLabel
%110 = OpLoad %bool %_0_return_on_both_sides
OpSelectionMerge %112 None
OpBranchConditional %110 %111 %112
%111 = OpLabel
%113 = OpFunctionCall %bool %for_inside_body
OpBranch %112
%112 = OpLabel
%114 = OpPhi %bool %false %109 %113 %111
OpSelectionMerge %116 None
OpBranchConditional %114 %115 %116
%115 = OpLabel
%117 = OpFunctionCall %bool %after_for_body
OpBranch %116
%116 = OpLabel
%118 = OpPhi %bool %false %112 %117 %115
OpSelectionMerge %120 None
OpBranchConditional %118 %119 %120
%119 = OpLabel
%121 = OpFunctionCall %bool %for_with_double_sided_conditional_return
OpBranch %120
%120 = OpLabel
%122 = OpPhi %bool %false %116 %121 %119
OpSelectionMerge %124 None
OpBranchConditional %122 %123 %124
%123 = OpLabel
%125 = OpFunctionCall %bool %if_else_chain
OpBranch %124
%124 = OpLabel
%126 = OpPhi %bool %false %120 %125 %123
OpSelectionMerge %131 None
OpBranchConditional %126 %129 %130
%129 = OpLabel
%132 = OpAccessChain %_ptr_Uniform_v4float %14 %int_0
%134 = OpLoad %v4float %132
OpStore %127 %134
OpBranch %131
%130 = OpLabel
%135 = OpAccessChain %_ptr_Uniform_v4float %14 %int_1
%136 = OpLoad %v4float %135
OpStore %127 %136
OpBranch %131
%131 = OpLabel
%137 = OpLoad %v4float %127
OpReturnValue %137
OpFunctionEnd

View File

@ -0,0 +1,30 @@
out vec4 sk_FragColor;
uniform vec4 colorGreen;
uniform vec4 colorRed;
uniform float unknownInput;
bool for_inside_body() {
for (int x = 0;x <= 10; ++x) {
return true;
}
}
bool after_for_body() {
for (int x = 0;x <= 10; ++x) {
}
return true;
}
bool for_with_double_sided_conditional_return() {
for (int x = 0;x <= 10; ++x) {
if (unknownInput == 1.0) return true; else return true;
}
}
bool if_else_chain() {
if (unknownInput == 1.0) return true; else if (unknownInput == 2.0) return false; else if (unknownInput == 3.0) return true; else if (unknownInput == 4.0) return false; else return true;
}
vec4 main() {
bool _0_return_on_both_sides;
if (unknownInput == 1.0) _0_return_on_both_sides = true; else _0_return_on_both_sides = true;
return (((_0_return_on_both_sides && for_inside_body()) && after_for_body()) && for_with_double_sided_conditional_return()) && if_else_chain() ? colorGreen : colorRed;
}

View File

@ -0,0 +1,44 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Uniforms {
float4 colorGreen;
float4 colorRed;
float unknownInput;
};
struct Inputs {
};
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
bool for_inside_body() {
for (int x = 0;x <= 10; ++x) {
return true;
}
}
bool after_for_body() {
for (int x = 0;x <= 10; ++x) {
}
return true;
}
bool for_with_double_sided_conditional_return(Uniforms _uniforms) {
for (int x = 0;x <= 10; ++x) {
if (_uniforms.unknownInput == 1.0) return true; else return true;
}
}
bool if_else_chain(Uniforms _uniforms) {
if (_uniforms.unknownInput == 1.0) return true; else if (_uniforms.unknownInput == 2.0) return false; else if (_uniforms.unknownInput == 3.0) return true; else if (_uniforms.unknownInput == 4.0) return false; else return true;
}
fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
bool _0_return_on_both_sides;
if (_uniforms.unknownInput == 1.0) _0_return_on_both_sides = true; else _0_return_on_both_sides = true;
_out.sk_FragColor = (((_0_return_on_both_sides && for_inside_body()) && after_for_body()) && for_with_double_sided_conditional_return(_uniforms)) && if_else_chain(_uniforms) ? _uniforms.colorGreen : _uniforms.colorRed;
return _out;
}

View File

@ -0,0 +1,633 @@
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %_entrypoint "_entrypoint" %sk_FragColor %sk_Clockwise
OpExecutionMode %_entrypoint OriginUpperLeft
OpName %sk_FragColor "sk_FragColor"
OpName %sk_Clockwise "sk_Clockwise"
OpName %_UniformBuffer "_UniformBuffer"
OpMemberName %_UniformBuffer 0 "colorGreen"
OpMemberName %_UniformBuffer 1 "colorRed"
OpMemberName %_UniformBuffer 2 "unknownInput"
OpName %_entrypoint "_entrypoint"
OpName %return_on_both_sides "return_on_both_sides"
OpName %for_inside_body "for_inside_body"
OpName %x "x"
OpName %after_for_body "after_for_body"
OpName %x_0 "x"
OpName %for_with_double_sided_conditional_return "for_with_double_sided_conditional_return"
OpName %x_1 "x"
OpName %if_else_chain "if_else_chain"
OpName %conditional_inside_while_loop "conditional_inside_while_loop"
OpName %inside_do_loop "inside_do_loop"
OpName %inside_while_loop "inside_while_loop"
OpName %after_do_loop "after_do_loop"
OpName %after_while_loop "after_while_loop"
OpName %switch_with_all_returns "switch_with_all_returns"
OpName %switch_only_default "switch_only_default"
OpName %switch_fallthrough "switch_fallthrough"
OpName %switch_fallthrough_twice "switch_fallthrough_twice"
OpName %switch_with_break_in_loop "switch_with_break_in_loop"
OpName %x_2 "x"
OpName %switch_with_continue_in_loop "switch_with_continue_in_loop"
OpName %x_3 "x"
OpName %switch_with_if_that_returns "switch_with_if_that_returns"
OpName %switch_with_one_sided_if_then_fallthrough "switch_with_one_sided_if_then_fallthrough"
OpName %main "main"
OpDecorate %sk_FragColor RelaxedPrecision
OpDecorate %sk_FragColor Location 0
OpDecorate %sk_FragColor Index 0
OpDecorate %sk_Clockwise RelaxedPrecision
OpDecorate %sk_Clockwise BuiltIn FrontFacing
OpMemberDecorate %_UniformBuffer 0 Offset 0
OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision
OpMemberDecorate %_UniformBuffer 1 Offset 16
OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision
OpMemberDecorate %_UniformBuffer 2 Offset 32
OpMemberDecorate %_UniformBuffer 2 RelaxedPrecision
OpDecorate %_UniformBuffer Block
OpDecorate %28 Binding 0
OpDecorate %28 DescriptorSet 0
OpDecorate %42 RelaxedPrecision
OpDecorate %85 RelaxedPrecision
OpDecorate %94 RelaxedPrecision
OpDecorate %100 RelaxedPrecision
OpDecorate %108 RelaxedPrecision
OpDecorate %115 RelaxedPrecision
OpDecorate %128 RelaxedPrecision
OpDecorate %157 RelaxedPrecision
OpDecorate %165 RelaxedPrecision
OpDecorate %171 RelaxedPrecision
OpDecorate %179 RelaxedPrecision
OpDecorate %187 RelaxedPrecision
OpDecorate %204 RelaxedPrecision
OpDecorate %221 RelaxedPrecision
OpDecorate %227 RelaxedPrecision
OpDecorate %234 RelaxedPrecision
OpDecorate %240 RelaxedPrecision
OpDecorate %325 RelaxedPrecision
OpDecorate %327 RelaxedPrecision
OpDecorate %328 RelaxedPrecision
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%sk_FragColor = OpVariable %_ptr_Output_v4float Output
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%_UniformBuffer = OpTypeStruct %v4float %v4float %float
%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
%28 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
%void = OpTypeVoid
%33 = OpTypeFunction %void
%36 = OpTypeFunction %bool
%_ptr_Uniform_float = OpTypePointer Uniform %float
%int = OpTypeInt 32 1
%int_2 = OpConstant %int 2
%float_1 = OpConstant %float 1
%true = OpConstantTrue %bool
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%int_1 = OpConstant %int 1
%float_2 = OpConstant %float 2
%false = OpConstantFalse %bool
%float_3 = OpConstant %float 3
%float_4 = OpConstant %float 4
%float_123 = OpConstant %float 123
%244 = OpTypeFunction %v4float
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_entrypoint = OpFunction %void None %33
%34 = OpLabel
%35 = OpFunctionCall %v4float %main
OpStore %sk_FragColor %35
OpReturn
OpFunctionEnd
%return_on_both_sides = OpFunction %bool None %36
%37 = OpLabel
%38 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%42 = OpLoad %float %38
%44 = OpFOrdEqual %bool %42 %float_1
OpSelectionMerge %47 None
OpBranchConditional %44 %45 %46
%45 = OpLabel
OpReturnValue %true
%46 = OpLabel
OpReturnValue %true
%47 = OpLabel
OpUnreachable
OpFunctionEnd
%for_inside_body = OpFunction %bool None %36
%49 = OpLabel
%x = OpVariable %_ptr_Function_int Function
OpStore %x %int_0
OpBranch %53
%53 = OpLabel
OpLoopMerge %57 %56 None
OpBranch %54
%54 = OpLabel
%58 = OpLoad %int %x
%60 = OpSLessThanEqual %bool %58 %int_10
OpBranchConditional %60 %55 %57
%55 = OpLabel
OpReturnValue %true
%56 = OpLabel
%62 = OpLoad %int %x
%63 = OpIAdd %int %62 %int_1
OpStore %x %63
OpBranch %53
%57 = OpLabel
OpUnreachable
OpFunctionEnd
%after_for_body = OpFunction %bool None %36
%64 = OpLabel
%x_0 = OpVariable %_ptr_Function_int Function
OpStore %x_0 %int_0
OpBranch %66
%66 = OpLabel
OpLoopMerge %70 %69 None
OpBranch %67
%67 = OpLabel
%71 = OpLoad %int %x_0
%72 = OpSLessThanEqual %bool %71 %int_10
OpBranchConditional %72 %68 %70
%68 = OpLabel
OpBranch %69
%69 = OpLabel
%73 = OpLoad %int %x_0
%74 = OpIAdd %int %73 %int_1
OpStore %x_0 %74
OpBranch %66
%70 = OpLabel
OpReturnValue %true
OpFunctionEnd
%for_with_double_sided_conditional_return = OpFunction %bool None %36
%75 = OpLabel
%x_1 = OpVariable %_ptr_Function_int Function
OpStore %x_1 %int_0
OpBranch %77
%77 = OpLabel
OpLoopMerge %81 %80 None
OpBranch %78
%78 = OpLabel
%82 = OpLoad %int %x_1
%83 = OpSLessThanEqual %bool %82 %int_10
OpBranchConditional %83 %79 %81
%79 = OpLabel
%84 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%85 = OpLoad %float %84
%86 = OpFOrdEqual %bool %85 %float_1
OpSelectionMerge %89 None
OpBranchConditional %86 %87 %88
%87 = OpLabel
OpReturnValue %true
%88 = OpLabel
OpReturnValue %true
%89 = OpLabel
OpBranch %80
%80 = OpLabel
%90 = OpLoad %int %x_1
%91 = OpIAdd %int %90 %int_1
OpStore %x_1 %91
OpBranch %77
%81 = OpLabel
OpUnreachable
OpFunctionEnd
%if_else_chain = OpFunction %bool None %36
%92 = OpLabel
%93 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%94 = OpLoad %float %93
%95 = OpFOrdEqual %bool %94 %float_1
OpSelectionMerge %98 None
OpBranchConditional %95 %96 %97
%96 = OpLabel
OpReturnValue %true
%97 = OpLabel
%99 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%100 = OpLoad %float %99
%102 = OpFOrdEqual %bool %100 %float_2
OpSelectionMerge %105 None
OpBranchConditional %102 %103 %104
%103 = OpLabel
OpReturnValue %false
%104 = OpLabel
%107 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%108 = OpLoad %float %107
%110 = OpFOrdEqual %bool %108 %float_3
OpSelectionMerge %113 None
OpBranchConditional %110 %111 %112
%111 = OpLabel
OpReturnValue %true
%112 = OpLabel
%114 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%115 = OpLoad %float %114
%117 = OpFOrdEqual %bool %115 %float_4
OpSelectionMerge %120 None
OpBranchConditional %117 %118 %119
%118 = OpLabel
OpReturnValue %false
%119 = OpLabel
OpReturnValue %true
%120 = OpLabel
OpBranch %113
%113 = OpLabel
OpBranch %105
%105 = OpLabel
OpBranch %98
%98 = OpLabel
OpUnreachable
OpFunctionEnd
%conditional_inside_while_loop = OpFunction %bool None %36
%121 = OpLabel
OpBranch %122
%122 = OpLabel
OpLoopMerge %126 %125 None
OpBranch %123
%123 = OpLabel
%127 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%128 = OpLoad %float %127
%130 = OpFOrdEqual %bool %128 %float_123
OpBranchConditional %130 %124 %126
%124 = OpLabel
OpReturnValue %true
%125 = OpLabel
OpBranch %122
%126 = OpLabel
OpUnreachable
OpFunctionEnd
%inside_do_loop = OpFunction %bool None %36
%131 = OpLabel
OpBranch %132
%132 = OpLabel
OpLoopMerge %136 %135 None
OpBranch %133
%133 = OpLabel
OpReturnValue %true
%134 = OpLabel
OpBranchConditional %true %135 %136
%135 = OpLabel
OpBranch %132
%136 = OpLabel
OpUnreachable
OpFunctionEnd
%inside_while_loop = OpFunction %bool None %36
%137 = OpLabel
OpBranch %138
%138 = OpLabel
OpLoopMerge %142 %141 None
OpBranch %139
%139 = OpLabel
OpBranchConditional %true %140 %142
%140 = OpLabel
OpReturnValue %true
%141 = OpLabel
OpBranch %138
%142 = OpLabel
OpUnreachable
OpFunctionEnd
%after_do_loop = OpFunction %bool None %36
%143 = OpLabel
OpBranch %144
%144 = OpLabel
OpLoopMerge %148 %147 None
OpBranch %145
%145 = OpLabel
OpBranch %148
%146 = OpLabel
OpBranchConditional %true %147 %148
%147 = OpLabel
OpBranch %144
%148 = OpLabel
OpReturnValue %true
OpFunctionEnd
%after_while_loop = OpFunction %bool None %36
%149 = OpLabel
OpBranch %150
%150 = OpLabel
OpLoopMerge %154 %153 None
OpBranch %151
%151 = OpLabel
OpBranchConditional %true %152 %154
%152 = OpLabel
OpBranch %154
%153 = OpLabel
OpBranch %150
%154 = OpLabel
OpReturnValue %true
OpFunctionEnd
%switch_with_all_returns = OpFunction %bool None %36
%155 = OpLabel
%156 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%157 = OpLoad %float %156
%158 = OpConvertFToS %int %157
OpSelectionMerge %159 None
OpSwitch %158 %162 1 %160 2 %161
%160 = OpLabel
OpReturnValue %true
%161 = OpLabel
OpReturnValue %true
%162 = OpLabel
OpReturnValue %true
%159 = OpLabel
OpUnreachable
OpFunctionEnd
%switch_only_default = OpFunction %bool None %36
%163 = OpLabel
%164 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%165 = OpLoad %float %164
%166 = OpConvertFToS %int %165
OpSelectionMerge %167 None
OpSwitch %166 %168
%168 = OpLabel
OpReturnValue %true
%167 = OpLabel
OpUnreachable
OpFunctionEnd
%switch_fallthrough = OpFunction %bool None %36
%169 = OpLabel
%170 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%171 = OpLoad %float %170
%172 = OpConvertFToS %int %171
OpSelectionMerge %173 None
OpSwitch %172 %176 1 %174 2 %175
%174 = OpLabel
OpReturnValue %true
%175 = OpLabel
OpBranch %176
%176 = OpLabel
OpReturnValue %true
%173 = OpLabel
OpUnreachable
OpFunctionEnd
%switch_fallthrough_twice = OpFunction %bool None %36
%177 = OpLabel
%178 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%179 = OpLoad %float %178
%180 = OpConvertFToS %int %179
OpSelectionMerge %181 None
OpSwitch %180 %184 1 %182 2 %183
%182 = OpLabel
OpBranch %183
%183 = OpLabel
OpBranch %184
%184 = OpLabel
OpReturnValue %true
%181 = OpLabel
OpUnreachable
OpFunctionEnd
%switch_with_break_in_loop = OpFunction %bool None %36
%185 = OpLabel
%x_2 = OpVariable %_ptr_Function_int Function
%186 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%187 = OpLoad %float %186
%188 = OpConvertFToS %int %187
OpSelectionMerge %189 None
OpSwitch %188 %191 1 %190
%190 = OpLabel
OpStore %x_2 %int_0
OpBranch %193
%193 = OpLabel
OpLoopMerge %197 %196 None
OpBranch %194
%194 = OpLabel
%198 = OpLoad %int %x_2
%199 = OpSLessThanEqual %bool %198 %int_10
OpBranchConditional %199 %195 %197
%195 = OpLabel
OpBranch %197
%196 = OpLabel
%200 = OpLoad %int %x_2
%201 = OpIAdd %int %200 %int_1
OpStore %x_2 %201
OpBranch %193
%197 = OpLabel
OpBranch %191
%191 = OpLabel
OpReturnValue %true
%189 = OpLabel
OpUnreachable
OpFunctionEnd
%switch_with_continue_in_loop = OpFunction %bool None %36
%202 = OpLabel
%x_3 = OpVariable %_ptr_Function_int Function
%203 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%204 = OpLoad %float %203
%205 = OpConvertFToS %int %204
OpSelectionMerge %206 None
OpSwitch %205 %208 1 %207
%207 = OpLabel
OpStore %x_3 %int_0
OpBranch %210
%210 = OpLabel
OpLoopMerge %214 %213 None
OpBranch %211
%211 = OpLabel
%215 = OpLoad %int %x_3
%216 = OpSLessThanEqual %bool %215 %int_10
OpBranchConditional %216 %212 %214
%212 = OpLabel
OpBranch %213
%213 = OpLabel
%217 = OpLoad %int %x_3
%218 = OpIAdd %int %217 %int_1
OpStore %x_3 %218
OpBranch %210
%214 = OpLabel
OpBranch %208
%208 = OpLabel
OpReturnValue %true
%206 = OpLabel
OpUnreachable
OpFunctionEnd
%switch_with_if_that_returns = OpFunction %bool None %36
%219 = OpLabel
%220 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%221 = OpLoad %float %220
%222 = OpConvertFToS %int %221
OpSelectionMerge %223 None
OpSwitch %222 %225 1 %224
%224 = OpLabel
%226 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%227 = OpLoad %float %226
%228 = OpFOrdEqual %bool %227 %float_123
OpSelectionMerge %231 None
OpBranchConditional %228 %229 %230
%229 = OpLabel
OpReturnValue %true
%230 = OpLabel
OpReturnValue %true
%231 = OpLabel
OpBranch %225
%225 = OpLabel
OpReturnValue %true
%223 = OpLabel
OpUnreachable
OpFunctionEnd
%switch_with_one_sided_if_then_fallthrough = OpFunction %bool None %36
%232 = OpLabel
%233 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%234 = OpLoad %float %233
%235 = OpConvertFToS %int %234
OpSelectionMerge %236 None
OpSwitch %235 %238 1 %237
%237 = OpLabel
%239 = OpAccessChain %_ptr_Uniform_float %28 %int_2
%240 = OpLoad %float %239
%241 = OpFOrdEqual %bool %240 %float_123
OpSelectionMerge %243 None
OpBranchConditional %241 %242 %243
%242 = OpLabel
OpReturnValue %true
%243 = OpLabel
OpBranch %238
%238 = OpLabel
OpReturnValue %true
%236 = OpLabel
OpUnreachable
OpFunctionEnd
%main = OpFunction %v4float None %244
%245 = OpLabel
%318 = OpVariable %_ptr_Function_v4float Function
OpSelectionMerge %247 None
OpBranchConditional %true %246 %247
%246 = OpLabel
%248 = OpFunctionCall %bool %return_on_both_sides
OpBranch %247
%247 = OpLabel
%249 = OpPhi %bool %false %245 %248 %246
OpSelectionMerge %251 None
OpBranchConditional %249 %250 %251
%250 = OpLabel
%252 = OpFunctionCall %bool %for_inside_body
OpBranch %251
%251 = OpLabel
%253 = OpPhi %bool %false %247 %252 %250
OpSelectionMerge %255 None
OpBranchConditional %253 %254 %255
%254 = OpLabel
%256 = OpFunctionCall %bool %after_for_body
OpBranch %255
%255 = OpLabel
%257 = OpPhi %bool %false %251 %256 %254
OpSelectionMerge %259 None
OpBranchConditional %257 %258 %259
%258 = OpLabel
%260 = OpFunctionCall %bool %for_with_double_sided_conditional_return
OpBranch %259
%259 = OpLabel
%261 = OpPhi %bool %false %255 %260 %258
OpSelectionMerge %263 None
OpBranchConditional %261 %262 %263
%262 = OpLabel
%264 = OpFunctionCall %bool %if_else_chain
OpBranch %263
%263 = OpLabel
%265 = OpPhi %bool %false %259 %264 %262
OpSelectionMerge %267 None
OpBranchConditional %265 %266 %267
%266 = OpLabel
%268 = OpFunctionCall %bool %conditional_inside_while_loop
OpBranch %267
%267 = OpLabel
%269 = OpPhi %bool %false %263 %268 %266
OpSelectionMerge %271 None
OpBranchConditional %269 %270 %271
%270 = OpLabel
%272 = OpFunctionCall %bool %inside_do_loop
OpBranch %271
%271 = OpLabel
%273 = OpPhi %bool %false %267 %272 %270
OpSelectionMerge %275 None
OpBranchConditional %273 %274 %275
%274 = OpLabel
%276 = OpFunctionCall %bool %inside_while_loop
OpBranch %275
%275 = OpLabel
%277 = OpPhi %bool %false %271 %276 %274
OpSelectionMerge %279 None
OpBranchConditional %277 %278 %279
%278 = OpLabel
%280 = OpFunctionCall %bool %after_do_loop
OpBranch %279
%279 = OpLabel
%281 = OpPhi %bool %false %275 %280 %278
OpSelectionMerge %283 None
OpBranchConditional %281 %282 %283
%282 = OpLabel
%284 = OpFunctionCall %bool %after_while_loop
OpBranch %283
%283 = OpLabel
%285 = OpPhi %bool %false %279 %284 %282
OpSelectionMerge %287 None
OpBranchConditional %285 %286 %287
%286 = OpLabel
%288 = OpFunctionCall %bool %switch_with_all_returns
OpBranch %287
%287 = OpLabel
%289 = OpPhi %bool %false %283 %288 %286
OpSelectionMerge %291 None
OpBranchConditional %289 %290 %291
%290 = OpLabel
%292 = OpFunctionCall %bool %switch_only_default
OpBranch %291
%291 = OpLabel
%293 = OpPhi %bool %false %287 %292 %290
OpSelectionMerge %295 None
OpBranchConditional %293 %294 %295
%294 = OpLabel
%296 = OpFunctionCall %bool %switch_fallthrough
OpBranch %295
%295 = OpLabel
%297 = OpPhi %bool %false %291 %296 %294
OpSelectionMerge %299 None
OpBranchConditional %297 %298 %299
%298 = OpLabel
%300 = OpFunctionCall %bool %switch_fallthrough_twice
OpBranch %299
%299 = OpLabel
%301 = OpPhi %bool %false %295 %300 %298
OpSelectionMerge %303 None
OpBranchConditional %301 %302 %303
%302 = OpLabel
%304 = OpFunctionCall %bool %switch_with_break_in_loop
OpBranch %303
%303 = OpLabel
%305 = OpPhi %bool %false %299 %304 %302
OpSelectionMerge %307 None
OpBranchConditional %305 %306 %307
%306 = OpLabel
%308 = OpFunctionCall %bool %switch_with_continue_in_loop
OpBranch %307
%307 = OpLabel
%309 = OpPhi %bool %false %303 %308 %306
OpSelectionMerge %311 None
OpBranchConditional %309 %310 %311
%310 = OpLabel
%312 = OpFunctionCall %bool %switch_with_if_that_returns
OpBranch %311
%311 = OpLabel
%313 = OpPhi %bool %false %307 %312 %310
OpSelectionMerge %315 None
OpBranchConditional %313 %314 %315
%314 = OpLabel
%316 = OpFunctionCall %bool %switch_with_one_sided_if_then_fallthrough
OpBranch %315
%315 = OpLabel
%317 = OpPhi %bool %false %311 %316 %314
OpSelectionMerge %322 None
OpBranchConditional %317 %320 %321
%320 = OpLabel
%323 = OpAccessChain %_ptr_Uniform_v4float %28 %int_0
%325 = OpLoad %v4float %323
OpStore %318 %325
OpBranch %322
%321 = OpLabel
%326 = OpAccessChain %_ptr_Uniform_v4float %28 %int_1
%327 = OpLoad %v4float %326
OpStore %318 %327
OpBranch %322
%322 = OpLabel
%328 = OpLoad %v4float %318
OpReturnValue %328
OpFunctionEnd

View File

@ -0,0 +1,128 @@
out vec4 sk_FragColor;
uniform vec4 colorGreen;
uniform vec4 colorRed;
uniform float unknownInput;
bool return_on_both_sides() {
if (unknownInput == 1.0) return true; else return true;
}
bool for_inside_body() {
for (int x = 0;x <= 10; ++x) {
return true;
}
}
bool after_for_body() {
for (int x = 0;x <= 10; ++x) {
true;
}
return true;
}
bool for_with_double_sided_conditional_return() {
for (int x = 0;x <= 10; ++x) {
if (unknownInput == 1.0) return true; else return true;
}
}
bool if_else_chain() {
if (unknownInput == 1.0) return true; else if (unknownInput == 2.0) return false; else if (unknownInput == 3.0) return true; else if (unknownInput == 4.0) return false; else return true;
}
bool conditional_inside_while_loop() {
while (unknownInput == 123.0) {
return true;
}
}
bool inside_do_loop() {
do {
return true;
} while (true);
}
bool inside_while_loop() {
while (true) {
return true;
}
}
bool after_do_loop() {
do {
break;
} while (true);
return true;
}
bool after_while_loop() {
while (true) {
break;
}
return true;
}
bool switch_with_all_returns() {
switch (int(unknownInput)) {
case 1:
return true;
case 2:
return true;
default:
return true;
}
}
bool switch_only_default() {
switch (int(unknownInput)) {
default:
return true;
}
}
bool switch_fallthrough() {
switch (int(unknownInput)) {
case 1:
return true;
case 2:
default:
return true;
}
}
bool switch_fallthrough_twice() {
switch (int(unknownInput)) {
case 1:
case 2:
default:
return true;
}
}
bool switch_with_break_in_loop() {
switch (int(unknownInput)) {
case 1:
for (int x = 0;x <= 10; ++x) {
break;
}
default:
return true;
}
}
bool switch_with_continue_in_loop() {
switch (int(unknownInput)) {
case 1:
for (int x = 0;x <= 10; ++x) {
continue;
}
default:
return true;
}
}
bool switch_with_if_that_returns() {
switch (int(unknownInput)) {
case 1:
if (unknownInput == 123.0) return true; else return true;
default:
return true;
}
}
bool switch_with_one_sided_if_then_fallthrough() {
switch (int(unknownInput)) {
case 1:
if (unknownInput == 123.0) return true;
default:
return true;
}
}
vec4 main() {
return (((((((((((((((((true && return_on_both_sides()) && for_inside_body()) && after_for_body()) && for_with_double_sided_conditional_return()) && if_else_chain()) && conditional_inside_while_loop()) && inside_do_loop()) && inside_while_loop()) && after_do_loop()) && after_while_loop()) && switch_with_all_returns()) && switch_only_default()) && switch_fallthrough()) && switch_fallthrough_twice()) && switch_with_break_in_loop()) && switch_with_continue_in_loop()) && switch_with_if_that_returns()) && switch_with_one_sided_if_then_fallthrough() ? colorGreen : colorRed;
}

View File

@ -0,0 +1,142 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Uniforms {
float4 colorGreen;
float4 colorRed;
float unknownInput;
};
struct Inputs {
};
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
bool return_on_both_sides(Uniforms _uniforms) {
if (_uniforms.unknownInput == 1.0) return true; else return true;
}
bool for_inside_body() {
for (int x = 0;x <= 10; ++x) {
return true;
}
}
bool after_for_body() {
for (int x = 0;x <= 10; ++x) {
true;
}
return true;
}
bool for_with_double_sided_conditional_return(Uniforms _uniforms) {
for (int x = 0;x <= 10; ++x) {
if (_uniforms.unknownInput == 1.0) return true; else return true;
}
}
bool if_else_chain(Uniforms _uniforms) {
if (_uniforms.unknownInput == 1.0) return true; else if (_uniforms.unknownInput == 2.0) return false; else if (_uniforms.unknownInput == 3.0) return true; else if (_uniforms.unknownInput == 4.0) return false; else return true;
}
bool conditional_inside_while_loop(Uniforms _uniforms) {
while (_uniforms.unknownInput == 123.0) {
return true;
}
}
bool inside_do_loop() {
do {
return true;
} while (true);
}
bool inside_while_loop() {
while (true) {
return true;
}
}
bool after_do_loop() {
do {
break;
} while (true);
return true;
}
bool after_while_loop() {
while (true) {
break;
}
return true;
}
bool switch_with_all_returns(Uniforms _uniforms) {
switch (int(_uniforms.unknownInput)) {
case 1:
return true;
case 2:
return true;
default:
return true;
}
}
bool switch_only_default(Uniforms _uniforms) {
switch (int(_uniforms.unknownInput)) {
default:
return true;
}
}
bool switch_fallthrough(Uniforms _uniforms) {
switch (int(_uniforms.unknownInput)) {
case 1:
return true;
case 2:
default:
return true;
}
}
bool switch_fallthrough_twice(Uniforms _uniforms) {
switch (int(_uniforms.unknownInput)) {
case 1:
case 2:
default:
return true;
}
}
bool switch_with_break_in_loop(Uniforms _uniforms) {
switch (int(_uniforms.unknownInput)) {
case 1:
for (int x = 0;x <= 10; ++x) {
break;
}
default:
return true;
}
}
bool switch_with_continue_in_loop(Uniforms _uniforms) {
switch (int(_uniforms.unknownInput)) {
case 1:
for (int x = 0;x <= 10; ++x) {
continue;
}
default:
return true;
}
}
bool switch_with_if_that_returns(Uniforms _uniforms) {
switch (int(_uniforms.unknownInput)) {
case 1:
if (_uniforms.unknownInput == 123.0) return true; else return true;
default:
return true;
}
}
bool switch_with_one_sided_if_then_fallthrough(Uniforms _uniforms) {
switch (int(_uniforms.unknownInput)) {
case 1:
if (_uniforms.unknownInput == 123.0) return true;
default:
return true;
}
}
fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
_out.sk_FragColor = (((((((((((((((((true && return_on_both_sides(_uniforms)) && for_inside_body()) && after_for_body()) && for_with_double_sided_conditional_return(_uniforms)) && if_else_chain(_uniforms)) && conditional_inside_while_loop(_uniforms)) && inside_do_loop()) && inside_while_loop()) && after_do_loop()) && after_while_loop()) && switch_with_all_returns(_uniforms)) && switch_only_default(_uniforms)) && switch_fallthrough(_uniforms)) && switch_fallthrough_twice(_uniforms)) && switch_with_break_in_loop(_uniforms)) && switch_with_continue_in_loop(_uniforms)) && switch_with_if_that_returns(_uniforms)) && switch_with_one_sided_if_then_fallthrough(_uniforms) ? _uniforms.colorGreen : _uniforms.colorRed;
return _out;
}