Emit prototypes in Pipeline for every defined function.

In complex programs with multiple functions, the Inliner can cause code
to be reordered in ways that cause a function call to be raised above
its declaration.

The Pipeline stage code generator will now emit a prototype for every
function defined in the program, before emitting any function bodies at
all.

With this change, ES2 conformance test `copy_global_inout_on_call` now
passes.

Change-Id: I85485710a34b778adef3cbc4a7ebe110a21a2a03
Bug: skia:12488
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/454742
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2021-09-30 17:02:57 -04:00 committed by SkCQ
parent 2f5cfb6af0
commit 6868f78d40
12 changed files with 64 additions and 16 deletions

View File

@ -69,7 +69,7 @@ private:
String functionName(const FunctionDeclaration& decl);
void writeFunction(const FunctionDefinition& f);
void writeFunctionPrototype(const FunctionPrototype& f);
void writeFunctionDeclaration(const FunctionDeclaration& decl);
String modifierString(const Modifiers& modifiers);
String functionDeclaration(const FunctionDeclaration& decl);
@ -102,7 +102,8 @@ private:
void writeReturnStatement(const ReturnStatement& r);
void writeSwitchStatement(const SwitchStatement& s);
void writeProgramElement(const ProgramElement& e);
void writeProgramElementFirstPass(const ProgramElement& e);
void writeProgramElementSecondPass(const ProgramElement& e);
struct AutoOutputBuffer {
AutoOutputBuffer(PipelineStageCodeGenerator* generator) : fGenerator(generator) {
@ -364,9 +365,10 @@ String PipelineStageCodeGenerator::functionDeclaration(const FunctionDeclaration
return declString + ")";
}
void PipelineStageCodeGenerator::writeFunctionPrototype(const FunctionPrototype& f) {
const FunctionDeclaration& decl = f.declaration();
fCallbacks->declareFunction(this->functionDeclaration(decl).c_str());
void PipelineStageCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& decl) {
if (!decl.isMain()) {
fCallbacks->declareFunction(this->functionDeclaration(decl).c_str());
}
}
void PipelineStageCodeGenerator::writeGlobalVarDeclaration(const GlobalVarDeclaration& g) {
@ -407,16 +409,17 @@ void PipelineStageCodeGenerator::writeStructDefinition(const StructDefinition& s
fCallbacks->defineStruct(definition.c_str());
}
void PipelineStageCodeGenerator::writeProgramElement(const ProgramElement& e) {
void PipelineStageCodeGenerator::writeProgramElementFirstPass(const ProgramElement& e) {
switch (e.kind()) {
case ProgramElement::Kind::kGlobalVar:
this->writeGlobalVarDeclaration(e.as<GlobalVarDeclaration>());
break;
case ProgramElement::Kind::kFunction:
this->writeFunction(e.as<FunctionDefinition>());
this->writeFunctionDeclaration(e.as<FunctionDefinition>().declaration());
break;
case ProgramElement::Kind::kFunctionPrototype:
this->writeFunctionPrototype(e.as<FunctionPrototype>());
// Skip this; we're already emitting prototypes for every FunctionDefinition.
// (See case kFunction, directly above.)
break;
case ProgramElement::Kind::kStructDefinition:
this->writeStructDefinition(e.as<StructDefinition>());
@ -431,6 +434,12 @@ void PipelineStageCodeGenerator::writeProgramElement(const ProgramElement& e) {
}
}
void PipelineStageCodeGenerator::writeProgramElementSecondPass(const ProgramElement& e) {
if (e.is<FunctionDefinition>()) {
this->writeFunction(e.as<FunctionDefinition>());
}
}
String PipelineStageCodeGenerator::typeName(const Type& type) {
if (type.isArray()) {
// This is necessary so that name mangling on arrays-of-structs works properly.
@ -738,20 +747,16 @@ void PipelineStageCodeGenerator::writeForStatement(const ForStatement& f) {
}
void PipelineStageCodeGenerator::generateCode() {
// Write all the program elements except for functions.
// Write all the program elements except for functions; prototype all the functions.
for (const ProgramElement* e : fProgram.elements()) {
if (!e->is<FunctionDefinition>()) {
this->writeProgramElement(*e);
}
this->writeProgramElementFirstPass(*e);
}
// We always place FunctionDefinition elements last, because the inliner likes to move function
// bodies around. After inlining, code can inadvertently move upwards, above ProgramElements
// that the code relies on.
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<FunctionDefinition>()) {
this->writeProgramElement(*e);
}
this->writeProgramElementSecondPass(*e);
}
}

View File

@ -1,4 +1,6 @@
const half r_0 = 0.0;
noinline half opt_barrier_0(const half x);
half2 compute_ba_0(const half2 rg);
noinline half opt_barrier_0(const half x)
{
return x;

View File

@ -1,5 +1,6 @@
float gInitializedFromOther_0 = 1.0;
float gUninitialized_0;
void init_globals_0();
void init_globals_0()
{
gUninitialized_0 = 1.0;

View File

@ -1,3 +1,7 @@
void d_0(inout int i);
void c_0(inout int i);
void b_0(inout int i);
void a_0(inout int i);
void d_0(inout int i)
{
++i;

View File

@ -1,3 +1,7 @@
void d_0(inout int i);
void c_0(inout int i);
void b_0(inout int i);
void a_0(inout int i);
void d_0(inout int i)
{
for (int x = 0;x < 10; ++x) ++i;

View File

@ -1,5 +1,11 @@
half4 color_0;
void f1_0();
void f8_0();
void f7_0();
void f6_0();
void f5_0();
void f4_0();
void f3_0();
void f2_0();
half4 main(float2 xy)
{
f2_0();

View File

@ -1,7 +1,17 @@
uniform half4 colorRed;
uniform half4 colorGreen;
const float kZero_0 = 0.0;
float return_loop_0();
const float kTen_0 = 10.0;
float continue_loop_0();
float break_loop_0();
float float_loop_0();
bool loop_operator_le_0();
bool loop_operator_lt_0();
bool loop_operator_ge_0();
bool loop_operator_gt_0();
bool loop_operator_ne_0();
bool loop_operator_eq_0();
float return_loop_0()
{
for (float i = kZero_0;i < 10.0; ++i)

View File

@ -1,7 +1,16 @@
uniform half4 colorRed;
uniform half4 colorGreen;
const int kZero_0 = 0;
int return_loop_0();
const int kTen_0 = 10;
int continue_loop_0();
int break_loop_0();
bool loop_operator_le_0();
bool loop_operator_lt_0();
bool loop_operator_ge_0();
bool loop_operator_gt_0();
bool loop_operator_ne_0();
bool loop_operator_eq_0();
int return_loop_0()
{
for (int i = kZero_0;i < 10; ++i)

View File

@ -1,5 +1,8 @@
uniform half4 colorGreen;
uniform half4 colorRed;
bool test_vector_0();
bool test_matrix_0();
bool test_array_0();
bool test_vector_0()
{
half2 mp2 = half2(2.0);

View File

@ -1,5 +1,6 @@
uniform half4 colorGreen;
uniform half4 colorRed;
bool switch_fallthrough_twice_0(int value);
bool switch_fallthrough_twice_0(int value)
{
bool ok = false;

View File

@ -1,5 +1,7 @@
uniform half4 colorGreen;
uniform half4 colorRed;
bool switch_with_continue_in_loop_0(int x);
bool loop_with_break_in_switch_0(int x);
bool switch_with_continue_in_loop_0(int x)
{
int val = 0;

View File

@ -2,6 +2,7 @@ uniform float4 u1;
uniform float4 u2;
uniform float4 u3;
uniform float4 u4;
float index_clamped_out_of_bounds_0();
float index_clamped_out_of_bounds_0()
{
for (int i = 7;i < 8; i++)