Preprocessor: Implement skipping of macros that evaluate to nothing while evaluating expressions. They had been causing early termination of an expression's evaluation.

Also includes some unrelated updates of documentation.


git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@24420 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich
2013-12-10 00:25:14 +00:00
parent c719481d43
commit 2d2f31625d
10 changed files with 120 additions and 50 deletions

View File

@@ -2,12 +2,12 @@ This directory contains linux binaries for the glslang validator. The main
executable is glslangValidator, which requires use of the shared object executable is glslangValidator, which requires use of the shared object
file libglsang.so. file libglsang.so.
Installation: Executing the ./install script will copy these to /usr/local/* Installation: Executing the ./install script will copy these to /usr/local/*
so that they may be executed from any directory. so that they may be executed from any directory.
Alternatively, you may put them where you wish, but will then need to use Alternatively, you may put them where you wish, but will then need to use
something like: something like:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<path to libglslang.so> export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<path to libglslang.so>
Usage: Execute glslangValidator with no arguments to get a usage statement. Usage: Execute glslangValidator with no arguments to get a usage statement.

Binary file not shown.

View File

@@ -66,22 +66,54 @@ shell.
Note: Despite appearances, the use of a DLL is currently disabled; it Note: Despite appearances, the use of a DLL is currently disabled; it
simply makes a standalone executable from a statically linked library. simply makes a standalone executable from a statically linked library.
Basic external programmatic interface Programmatic Interfaces
------------------------------------- -----------------------
Another piece of software can programmatically translate shaders to an AST Another piece of software can programmatically translate shaders to an AST
using the C-style ShInitialize(), ShCompile(), et. al. interface. The main() in using one of two different interfaces:
StandAlone/StandAlone.cpp shows an example way of using these. - A new C++ class-oriented interface, or
- The original C functional interface
The Sh*() interface takes a "compiler" call-back object, which it calls after The main() in StandAlone/StandAlone.cpp shows examples using both styles.
building call back that is passed the AST and can then execute a backend on it.
The following is a simplified resulting run-time call stack: C++ Class Interface (new, preferred):
ShCompile(shader, compiler) -> compiler(AST) -> <back end> This interface is in roughly the last 1/3 of ShaderLang.h. It is in the
glslang namespace and contains the following.
In practice, ShCompile() takes shader strings, default version, and const char* GetEsslVersionString();
warning/error and other options for controling compilation. const char* GetGlslVersionString();
bool InitializeProcess();
void FinalizeProcess();
class TShader
bool parse(...);
void setStrings(...);
const char* getInfoLog();
class TProgram
void addShader(...);
bool link(...);
const char* getInfoLog();
Reflection queries
See ShaderLang.h and the usage of it in StandAlone/StandAlone.cpp for more
details.
C Functional Interface (orginal):
This interface is in roughly the first 2/3 of ShaderLang.h, and referred to
as the Sh*() interface, as all the entry points start "Sh".
The Sh*() interface takes a "compiler" call-back object, which it calls after
building call back that is passed the AST and can then execute a backend on it.
The following is a simplified resulting run-time call stack:
ShCompile(shader, compiler) -> compiler(AST) -> <back end>
In practice, ShCompile() takes shader strings, default version, and
warning/error and other options for controling compilation.
Testing Testing
------- -------
@@ -100,7 +132,7 @@ missing, those tests just won't run.
Basic Internal Operation Basic Internal Operation
------------------------ ------------------------
- Initial lexical analysis is done be the preprocessor in - Initial lexical analysis is done by the preprocessor in
MachineIndependent/Preprocessor, and then refined by a GLSL scanner MachineIndependent/Preprocessor, and then refined by a GLSL scanner
in MachineIndependent/Scan.cpp. There is currently no use of flex. in MachineIndependent/Scan.cpp. There is currently no use of flex.

View File

@@ -67,12 +67,15 @@ ERROR: 7:14014: '#error' : line should be 14014 , string 7
ERROR: 12:14014: '#error' : line should be 14014 , string 12 ERROR: 12:14014: '#error' : line should be 14014 , string 12
ERROR: 12:14026: '#error' : line should be 14026 , string 12 ERROR: 12:14026: '#error' : line should be 14026 , string 12
ERROR: 12:1234: '#line' : unexpected tokens following directive ERROR: 12:1234: '#line' : unexpected tokens following directive
ERROR: 12:1237: '#line' : unexpected tokens following directive
ERROR: 12:20001: '#error' : line should be 20001 ERROR: 12:20001: '#error' : line should be 20001
ERROR: 12:20011: '#error' : line should be 20011 ERROR: 12:20011: '#error' : line should be 20011
ERROR: 12:20021: '#error' : line should be 20021 ERROR: 12:20021: '#error' : line should be 20021
ERROR: 12:20046: '#define' : Macro redefined; different substitutions: SPACE_IN_MIDDLE ERROR: 12:20046: '#define' : Macro redefined; different substitutions: SPACE_IN_MIDDLE
ERROR: 12:20052: '#error' : good evaluation 1
ERROR: 12:20056: '#error' : good evaluation 2
ERROR: 12:10003: '' : missing #endif ERROR: 12:10003: '' : missing #endif
ERROR: 72 compilation errors. No code generated. ERROR: 75 compilation errors. No code generated.
ERROR: node is still EOpNull! ERROR: node is still EOpNull!

View File

@@ -251,11 +251,18 @@ double f = f1;
#define F2 7 #define F2 7
#line L1 + L2 #line L1 + L2
#error line should be 14014, string 7 #error line should be 14014, string 7
#line L1 + L2 F1 + F2 #line L1 + L2 F1 + F2 // antoeuh sat comment
#error line should be 14014, string 12 #error line should be 14014, string 12
#line L1 + L2 + F1 + F2 #line L1 + L2 + F1 + F2
#error line should be 14026, string 12 #error line should be 14026, string 12
#line 1234 F1 + F2 extra #line 1234 F1 + F2 extra
#define empty_extra
#line 1235 F1 + F2 empty_extra
#define moreEmpty empty_extra
#line 1236 F1 + F2 moreEmpty empty_extra // okay, lots of nothin
#line 1237 F1 + F2 moreEmpty empty_extra extra // ERROR, 'extra'
#line 1238 F1 + F2 moreEmpty empty_extra
#line 1239 empty_extra F1 empty_extra + empty_extra F2 empty_extra moreEmpty empty_extra
#line (20000) #line (20000)
#error line should be 20001 #error line should be 20001
#line (20000+10) #line (20000+10)
@@ -288,6 +295,17 @@ void foo234()
#define SPACE_IN_MIDDLE(a,b) space +in middle #define SPACE_IN_MIDDLE(a,b) space +in middle
#define SPACE_IN_MIDDLE(a,b) space + in middle #define SPACE_IN_MIDDLE(a,b) space + in middle
#define FIRSTPART 17
#define SECONDPART + 5
#if FIRSTPART SECONDPART == 22
#error good evaluation 1
#endif
#if moreEmpty FIRSTPART moreEmpty SECONDPART moreEmpty == moreEmpty 22 moreEmpty
#error good evaluation 2
#endif
#line 10000 #line 10000
#if 1 #if 1
#else #else

View File

@@ -9,5 +9,5 @@
// source have to figure out how to create revision.h just to get a build // source have to figure out how to create revision.h just to get a build
// going. However, if it is not updated, it can be a version behind. // going. However, if it is not updated, it can be a version behind.
#define GLSLANG_REVISION "24400" #define GLSLANG_REVISION "24406"
#define GLSLANG_DATE "2013/12/06 17:28:07" #define GLSLANG_DATE "2013/12/08 17:37:46"

View File

@@ -376,7 +376,7 @@ bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNo
} }
// A metecheck on the condition the compiler itself... // A metecheck on the condition the compiler itself...
switch(version) { switch (version) {
case 100: case 100:
case 300: case 300:

View File

@@ -473,28 +473,8 @@ int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, boo
token = currentInput->scan(this, currentInput, ppToken); token = currentInput->scan(this, currentInput, ppToken);
} }
} else { } else {
int macroReturn = MacroExpand(ppToken->atom, ppToken, 1); token = evalToToken(token, shortCircuit, res, err, ppToken);
if (macroReturn == 0) { return eval(token, precedence, shortCircuit, res, err, ppToken);
parseContext.error(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", "");
err = true;
res = 0;
return token;
} else {
if (macroReturn == -1) {
if (! shortCircuit && parseContext.profile == EEsProfile) {
const char* message = "undefined macro in expression not allowed in es profile";
const char* name = GetAtomString(ppToken->atom);
if (parseContext.messages & EShMsgRelaxedErrors)
parseContext.warn(ppToken->loc, message, "preprocessor evaluation", name);
else
parseContext.error(ppToken->loc, message, "preprocessor evaluation", name);
}
}
token = currentInput->scan(this, currentInput, ppToken);
return eval(token, precedence, shortCircuit, res, err, ppToken);
}
} }
} else if (token == CPP_INTCONSTANT) { } else if (token == CPP_INTCONSTANT) {
res = ppToken->ival; res = ppToken->ival;
@@ -530,6 +510,10 @@ int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, boo
return token; return token;
} }
} }
token = evalToToken(token, shortCircuit, res, err, ppToken);
// Perform evaluation of binary operation, if there is one, otherwise we are done.
while (! err) { while (! err) {
if (token == ')' || token == '\n') if (token == ')' || token == '\n')
break; break;
@@ -558,6 +542,33 @@ int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, boo
return token; return token;
} }
// Expand macros, skipping empty expansions, to get to the first real token in those expansions.
int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken* ppToken)
{
while (token == CPP_IDENTIFIER && ppToken->atom != definedAtom) {
int macroReturn = MacroExpand(ppToken->atom, ppToken, true);
if (macroReturn == 0) {
parseContext.error(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", "");
err = true;
res = 0;
break;
}
if (macroReturn == -1) {
if (! shortCircuit && parseContext.profile == EEsProfile) {
const char* message = "undefined macro in expression not allowed in es profile";
const char* name = GetAtomString(ppToken->atom);
if (parseContext.messages & EShMsgRelaxedErrors)
parseContext.warn(ppToken->loc, message, "preprocessor evaluation", name);
else
parseContext.error(ppToken->loc, message, "preprocessor evaluation", name);
}
}
token = currentInput->scan(this, currentInput, ppToken);
}
return token;
}
// Handle #if // Handle #if
int TPpContext::CPPif(TPpToken* ppToken) int TPpContext::CPPif(TPpToken* ppToken)
{ {
@@ -612,6 +623,10 @@ int TPpContext::CPPifdef(int defined, TPpToken* ppToken)
// Handle #line // Handle #line
int TPpContext::CPPline(TPpToken* ppToken) int TPpContext::CPPline(TPpToken* ppToken)
{ {
// "#line must have, after macro substitution, one of the following forms:
// "#line line
// "#line line source-string-number"
int token = currentInput->scan(this, currentInput, ppToken); int token = currentInput->scan(this, currentInput, ppToken);
if (token == '\n') { if (token == '\n') {
parseContext.error(ppToken->loc, "must by followed by an integral literal", "#line", ""); parseContext.error(ppToken->loc, "must by followed by an integral literal", "#line", "");
@@ -633,6 +648,7 @@ int TPpContext::CPPline(TPpToken* ppToken)
parseContext.setCurrentString(fileRes); parseContext.setCurrentString(fileRes);
} }
} }
token = extraTokenCheck(lineAtom, ppToken, token); token = extraTokenCheck(lineAtom, ppToken, token);
return token; return token;
@@ -886,7 +902,7 @@ TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream *a, TPpToken* p
PushEofSrc(); PushEofSrc();
ReadFromTokenStream(a, 0, 0); ReadFromTokenStream(a, 0, 0);
while ((token = currentInput->scan(this, currentInput, ppToken)) > 0) { while ((token = currentInput->scan(this, currentInput, ppToken)) > 0) {
if (token == CPP_IDENTIFIER && MacroExpand(ppToken->atom, ppToken, 0) == 1) if (token == CPP_IDENTIFIER && MacroExpand(ppToken->atom, ppToken, false) == 1)
continue; continue;
RecordToken(n, token, ppToken); RecordToken(n, token, ppToken);
} }
@@ -952,13 +968,13 @@ int TPpContext::zero_scan(TPpContext* pp, InputSrc *inInput, TPpToken* ppToken)
/* /*
** Check an identifier (atom) to see if it is a macro that should be expanded. ** Check an identifier (atom) to see if it is a macro that should be expanded.
** If it is, push an InputSrc that will produce the appropriate expansion ** If it is, and defined, push an InputSrc that will produce the appropriate expansion
** and return 1. ** and return 1.
** If it is, but undefined, it should expand to 0, push an InputSrc that will ** If it is, but undefined, and expandUndef is requested, push an InputSrc that will
** expand to 0 and return -1. ** expand to 0 and return -1.
** Otherwise, return 0. ** Otherwise, return 0.
*/ */
int TPpContext::MacroExpand(int atom, TPpToken* ppToken, int expandUndef) int TPpContext::MacroExpand(int atom, TPpToken* ppToken, bool expandUndef)
{ {
Symbol *sym = LookUpSymbol(atom); Symbol *sym = LookUpSymbol(atom);
MacroInputSrc *in; MacroInputSrc *in;

View File

@@ -247,6 +247,7 @@ protected:
int CPPelse(int matchelse, TPpToken * ppToken); int CPPelse(int matchelse, TPpToken * ppToken);
int extraTokenCheck(int atom, TPpToken* ppToken, int token); int extraTokenCheck(int atom, TPpToken* ppToken, int token);
int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken);
int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken);
int CPPif (TPpToken * ppToken); int CPPif (TPpToken * ppToken);
int CPPifdef(int defined, TPpToken * ppToken); int CPPifdef(int defined, TPpToken * ppToken);
int CPPline(TPpToken * ppToken); int CPPline(TPpToken * ppToken);
@@ -260,7 +261,7 @@ protected:
TokenStream* PrescanMacroArg(TokenStream *a, TPpToken * ppToken); TokenStream* PrescanMacroArg(TokenStream *a, TPpToken * ppToken);
static int macro_scan(TPpContext* pp, InputSrc *inInput, TPpToken * ppToken); static int macro_scan(TPpContext* pp, InputSrc *inInput, TPpToken * ppToken);
static int zero_scan(TPpContext* pp, InputSrc *inInput, TPpToken * ppToken); static int zero_scan(TPpContext* pp, InputSrc *inInput, TPpToken * ppToken);
int MacroExpand(int atom, TPpToken* ppToken, int expandUndef); int MacroExpand(int atom, TPpToken* ppToken, bool expandUndef);
// //
// from PpSymbols.cpp // from PpSymbols.cpp

View File

@@ -744,7 +744,7 @@ const char* TPpContext::tokenize(TPpToken* ppToken)
continue; continue;
// expand macros // expand macros
if (token == CPP_IDENTIFIER && MacroExpand(ppToken->atom, ppToken, 0) == 1) if (token == CPP_IDENTIFIER && MacroExpand(ppToken->atom, ppToken, false) == 1)
continue; continue;
if (token == CPP_IDENTIFIER) if (token == CPP_IDENTIFIER)