// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Compiler.h"
#include "AnalyzeCallDepth.h"
#include "Initialize.h"
#include "InitializeParseContext.h"
#include "InitializeGlobals.h"
#include "ParseHelper.h"
#include "ValidateLimitations.h"
namespace
{
class TScopedPoolAllocator {
public:
TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop)
: mAllocator(allocator), mPushPopAllocator(pushPop)
{
if (mPushPopAllocator) mAllocator->push();
SetGlobalPoolAllocator(mAllocator);
}
~TScopedPoolAllocator()
{
SetGlobalPoolAllocator(nullptr);
if (mPushPopAllocator) mAllocator->pop();
}
private:
TPoolAllocator* mAllocator;
bool mPushPopAllocator;
};
} // namespace
//
// Initialize built-in resources with minimum expected values.
//
ShBuiltInResources::ShBuiltInResources()
{
// Constants.
MaxVertexAttribs = 8;
MaxVertexUniformVectors = 128;
MaxVaryingVectors = 8;
MaxVertexTextureImageUnits = 0;
MaxCombinedTextureImageUnits = 8;
MaxTextureImageUnits = 8;
MaxFragmentUniformVectors = 16;
MaxDrawBuffers = 1;
MaxVertexOutputVectors = 16;
MaxFragmentInputVectors = 15;
MinProgramTexelOffset = -8;
MaxProgramTexelOffset = 7;
// Extensions.
OES_standard_derivatives = 0;
OES_fragment_precision_high = 0;
OES_EGL_image_external = 0;
MaxCallStackDepth = UINT_MAX;
}
TCompiler::TCompiler(GLenum type)
: shaderType(type),
maxCallStackDepth(UINT_MAX)
{
allocator.push();
SetGlobalPoolAllocator(&allocator);
}
TCompiler::~TCompiler()
{
SetGlobalPoolAllocator(nullptr);
allocator.popAll();
}
bool TCompiler::Init(const ShBuiltInResources& resources)
{
shaderVersion = 100;
maxCallStackDepth = resources.MaxCallStackDepth;
TScopedPoolAllocator scopedAlloc(&allocator, false);
// Generate built-in symbol table.
if (!InitBuiltInSymbolTable(resources))
return false;
InitExtensionBehavior(resources, extensionBehavior);
return true;
}
bool TCompiler::compile(const char* const shaderStrings[],
const int numStrings,
int compileOptions)
{
TScopedPoolAllocator scopedAlloc(&allocator, true);
clearResults();
if (numStrings == 0)
return true;
// First string is path of source file if flag is set. The actual source follows.
const char* sourcePath = nullptr;
int firstSource = 0;
if (compileOptions & SH_SOURCE_PATH)
{
sourcePath = shaderStrings[0];
++firstSource;
}
TIntermediate intermediate(infoSink);
TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
shaderType, compileOptions, true,
sourcePath, infoSink);
SetGlobalParseContext(&parseContext);
// We preserve symbols at the built-in level from compile-to-compile.
// Start pushing the user-defined symbols at global level.
symbolTable.push();
if (!symbolTable.atGlobalLevel())
infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
// Parse shader.
bool success =
(PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, &parseContext) == 0) &&
(parseContext.getTreeRoot() != nullptr);
shaderVersion = parseContext.getShaderVersion();
if (success) {
TIntermNode* root = parseContext.getTreeRoot();
success = intermediate.postProcess(root);
if (success)
success = validateCallDepth(root, infoSink);
if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
success = validateLimitations(root);
if (success && (compileOptions & SH_INTERMEDIATE_TREE))
intermediate.outputTree(root);
if (success && (compileOptions & SH_OBJECT_CODE))
success = translate(root);
}
// Ensure symbol table is returned to the built-in level,
// throwing away all but the built-ins.
while (!symbolTable.atBuiltInLevel())
symbolTable.pop();
return success;
}
bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
{
assert(symbolTable.isEmpty());
symbolTable.push(); // COMMON_BUILTINS
symbolTable.push(); // ESSL1_BUILTINS
symbolTable.push(); // ESSL3_BUILTINS
TPublicType integer;
integer.type = EbtInt;
integer.primarySize = 1;
integer.secondarySize = 1;
integer.array = false;
TPublicType floatingPoint;
floatingPoint.type = EbtFloat;
floatingPoint.primarySize = 1;
floatingPoint.secondarySize = 1;
floatingPoint.array = false;
switch(shaderType)
{
case GL_FRAGMENT_SHADER:
symbolTable.setDefaultPrecision(integer, EbpMedium);
break;
case GL_VERTEX_SHADER:
symbolTable.setDefaultPrecision(integer, EbpHigh);
symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
break;
default: assert(false && "Language not supported");
}
InsertBuiltInFunctions(shaderType, resources, symbolTable);
IdentifyBuiltIns(shaderType, resources, symbolTable);
return true;
}
void TCompiler::clearResults()
{
infoSink.info.erase();
infoSink.obj.erase();
infoSink.debug.erase();
}
bool TCompiler::validateCallDepth(TIntermNode *root, TInfoSink &infoSink)
{
AnalyzeCallDepth validator(root);
unsigned int depth = validator.analyzeCallDepth();
if(depth == 0)
{
infoSink.info.prefix(EPrefixError);
infoSink.info << "Missing main()";
return false;
}
else if(depth == UINT_MAX)
{
infoSink.info.prefix(EPrefixError);
infoSink.info << "Function recursion detected";
return false;
}
else if(depth > maxCallStackDepth)
{
infoSink.info.prefix(EPrefixError);
infoSink.info << "Function call stack too deep (depth was ";
infoSink.info << depth;
infoSink.info << " while maximum call stack depth is ";
infoSink.info << maxCallStackDepth;
infoSink.info << ")";
return false;
}
return true;
}
bool TCompiler::validateLimitations(TIntermNode* root) {
ValidateLimitations validate(shaderType, infoSink.info);
root->traverse(&validate);
return validate.numErrors() == 0;
}
const TExtensionBehavior& TCompiler::getExtensionBehavior() const
{
return extensionBehavior;
}
bool InitCompilerGlobals()
{
if(!InitializePoolIndex())
{
assert(0 && "InitCompilerGlobals(): Failed to initalize global pool");
return false;
}
if(!InitializeParseContextIndex())
{
assert(0 && "InitCompilerGlobals(): Failed to initalize parse context");
return false;
}
return true;
}
void FreeCompilerGlobals()
{
FreeParseContextIndex();
FreePoolIndex();
}