diff options
Diffstat (limited to 'o3d/compiler')
36 files changed, 9742 insertions, 0 deletions
diff --git a/o3d/compiler/antlr/build.scons b/o3d/compiler/antlr/build.scons new file mode 100644 index 0000000..a6d7df5 --- /dev/null +++ b/o3d/compiler/antlr/build.scons @@ -0,0 +1,69 @@ +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Import('env') + +env.Append(CPPPATH = env['RENDERER_INCLUDE_PATH'] + ['$ANTLRLIBC_DIR/include']) + +if env.Bit('windows'): + # force these files to be compiled as C + env.Append(CCFLAGS = ['/TC']) + +# Create the Antlr C Runtime library ------------------------------------------- +antlr_sources = [ + 'antlr3baserecognizer', + 'antlr3basetree', + 'antlr3basetreeadaptor', + 'antlr3bitset', + 'antlr3collections', + 'antlr3commontoken', + 'antlr3commontree', + 'antlr3commontreeadaptor', + 'antlr3commontreenodestream', + 'antlr3convertutf', + 'antlr3cyclicdfa', + 'antlr3debughandlers', + 'antlr3encodings', + 'antlr3exception', + 'antlr3filestream', + 'antlr3inputstream', + 'antlr3intstream', + 'antlr3lexer', + 'antlr3parser', + 'antlr3rewritestreams', + 'antlr3string', + 'antlr3stringstream', + 'antlr3tokenstream', + 'antlr3treeparser', + 'antlr3ucs2inputstream', +] +antlr_objects = env.MakeObjects(antlr_sources, '$ANTLRLIBC_DIR/src', 'c') + +antlr_lib = env.ComponentLibrary('antlr3c', antlr_objects) diff --git a/o3d/compiler/hlsl/HLSL.g b/o3d/compiler/hlsl/HLSL.g new file mode 100644 index 0000000..7477b0d --- /dev/null +++ b/o3d/compiler/hlsl/HLSL.g @@ -0,0 +1,1020 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the ANTLR grammar for parsing HLSL into an Abstract +// Syntax Tree (AST). + +grammar HLSL; + +options { + language = Java; + } + +@header +{ +} + +translation_unit + : ( global_declaration )* (technique)* EOF + ; + +global_declaration + : (function_storage_class? function_type_specifier ID LPAREN) => function_declaration + | sampler_declaration + | texture_declaration + | struct_definition + | typedef_definition + | var_declaration + ; + +// variables ------------------------------------------- + +var_declaration + : var_storage_class* + var_type_modifier? + var_datatype id_declaration + semantic? + annotation_list? + ('=' initializer)? + var_packoffset? + var_register_bind? + SEMI + ; + +var_storage_class + : 'extern' + | 'nointerpolation' + | 'shared' + | 'static' + | 'uniform' + | 'volatile' + ; + +var_type_modifier + : ('const'|'row_major'|'column_major'); + +var_datatype + : buffer_type_specifier + | scalar_type_or_string_specifier + | vector_type_specifier + | matrix_type_specifier + | struct_type_specifier + ; + +var_packoffset + : 'packoffset' LPAREN register_name (DOT vector_subcomponent)? RPAREN + ; + +var_register_bind + : COLON register_name + ; + +id_declaration + : ID ( LBRACKET constant_expression RBRACKET )? + ; + +// function -------------------------------------------- + +function_declaration + : function_storage_class? + function_type_specifier ID LPAREN argument_list RPAREN semantic? + function_body (SEMI)? + ; + +function_storage_class + : 'inline' // ignoring platform target + ; + +function_type_specifier + : scalar_type_specifier + | vector_type_specifier + | matrix_type_specifier + | struct_type_specifier + | 'void' + ; + +semantic + : COLON semantic_specifier ; + +param_type_specifier + : scalar_type_specifier + | vector_type_specifier + | matrix_type_specifier + | struct_type_specifier + | string_type_specifier + ; + +// typedef --------------------------------------- + +typedef_definition + : 'typedef' + ; + +// basic datatypes ------------------------------- + +buffer_type_specifier + : ('buffer' '<' var_datatype '>' ID) + ; + +scalar_type_specifier + : 'bool' + | 'int' + | 'uint' + | 'half' + | 'float' + | 'double' + ; + +scalar_type_or_string_specifier + : scalar_type_specifier + | string_type_specifier + ; + +vector_type_specifier + : 'bool1' + | 'bool2' + | 'bool3' + | 'bool4' + | 'int1' + | 'int2' + | 'int3' + | 'int4' + | 'uint1' + | 'uint2' + | 'uint3' + | 'uint4' + | 'half1' + | 'half2' + | 'half3' + | 'half4' + | 'float1' + | 'float2' + | 'float3' + | 'float4' + | 'double1' + | 'double2' + | 'double3' + | 'double4' + | 'vector' '<' scalar_type_specifier ',' ('1'|'2'|'3'|'4') '>' + ; + +matrix_type_specifier + : 'float1x1' + | 'float1x2' + | 'float1x3' + | 'float1x4' + | 'float2x1' + | 'float2x2' + | 'float2x3' + | 'float2x4' + | 'float3x1' + | 'float3x2' + | 'float3x3' + | 'float3x4' + | 'float4x1' + | 'float4x2' + | 'float4x3' + | 'float4x4' + | 'matrix' '<' scalar_type_specifier ',' ('1'|'2'|'3'|'4') ',' ('1'|'2'|'3'|'4') '>' + ; + +string_type_specifier + : 'string' + ; + +// Sampler declarations ---------------------------- + +sampler_declaration + : SAMPLER ID '=' sampler_type_specifier '{' sampler_state_declaration+ '}' SEMI + | SAMPLER ID SEMI // GLSL format + | sampler_type_specifier id_declaration '=' '{' sampler_state_declaration_simple ']' SEMI // DX10 style + ; + +sampler_state_declaration + : ( ('Texture'|'texture') '=' '<' ID '>' SEMI ) // DX9 must have one of these + | sampler_state_declaration_simple + ; + +sampler_state_declaration_simple + : ( sampler_state_specifier '=' initializer SEMI ) // DX10 style + ; + +sampler_type_specifier + : 'sampler' | 'Sampler' + | 'sampler1D' | 'Sampler1D' | 'sampler1d' + | 'sampler2D' | 'Sampler2D' | 'sampler2d' + | 'sampler3D' | 'Sampler3D' | 'sampler3d' + | 'samplerCUBE' | 'SamplerCUBE' | 'samplercube' + | 'sampler_state' | 'samplerstate' | 'SamplerState' + | 'SamplerComparisonState' | 'samplercomparisonstate' // DX10 only + ; + +sampler_state_specifier + : 'AddressU' | 'addressu' + | 'AddressV' | 'addressv' + | 'AddressW' | 'addressw' + | 'BorderColor' | 'bordercolor' + | 'ComparisonFilter' | 'comparisonfilter' // SamplerComparisonState only + | 'ComparisonFunc' | 'comparisonfunc' // SamplerComparisonState only + | 'MagFilter' | 'magfilter' + | 'MaxAnisotropy' | 'maxanisotropy' + | 'MinFilter' | 'minfilter' + | 'MipFilter' | 'mipfilter' + | 'MipMapLODBias' | 'MipMapLodBias' | 'mipmapLODbias' | 'mipmaplodbias' + ; + + // texture declaration ---------------------------- + +texture_declaration + : texture_type_specifier ID annotation_list? SEMI + | texture_type_specifier '<' + (scalar_type_specifier | vector_type_specifier ) '>' ID SEMI // GLSL syntax. + ; + +texture_type_specifier + : 'texture' + | 'texture1D' | 'Texture1D' + | 'texture2D' | 'Texture2D' + | 'texture3D' | 'Texture3D' + | 'textureCUBE' | 'TextureCUBE' + ; + + // struct declaration ----------------------------- + +struct_type_specifier + : ID + | ( STRUCT ( ID )? LCURLY ) => struct_definition + | STRUCT ID + ; + +annotation_list + : '<' (annotation)* '>'; + +annotation + : scalar_type_or_string_specifier ID '=' constant_expression SEMI ; + +initializer + : constant_expression + | '{' constant_expression ( ',' constant_expression )* '}' + ; + +register_name + // registers for VS_3_0 + : 'register' '(' input_register_name | output_register_name ')'; + +input_register_name + : (('v'|'r'|'c'|'b'|'i'|'s'|'o') DECIMAL_LITERAL) + | ('a0'|'aL'|'p0') + ; + +output_register_name + : 'oD0'|'oD1'|'oFog'|'oPos'|'oPts' + | 'oT0'|'oT1'|'oT2'|'oT3'|'oT4'|'oT5'|'oT6'|'oT7' + ; + +pack_offset + : .+ ; // no idea what this field looks like. + +argument_list + : ( param_declaration ( COMMA param_declaration )* )? + ; + +param_declaration + : param_direction? param_variability? param_type_specifier id_declaration semantic? + | SAMPLER ID +// | FUNCTION type_specifier ID + ; + +param_variability + : CONST + | UNIFORM + ; + +param_direction + : IN + | OUT + | INOUT + ; + +function_body + : LCURLY ( decl_or_statement )* RCURLY + ; + +decl_or_statement + // We copied the following sub-rule here to expedite the parsing time + // as this is a much more common case than the "Id init_declarator_list" + // case which would normally spend a lot of time in exception handling. + : (lvalue_expression assignment_operator ) => assignment_statement + | ( ( CONST )? vector_type_specifier ) => ( CONST )? vector_type_specifier init_declarator_list SEMI + | ( ( CONST )? scalar_type_specifier ) => ( CONST )? scalar_type_specifier init_declarator_list SEMI + | ( STRUCT ( ID )? LCURLY ) => struct_definition ( init_declarator_list )? SEMI + | STRUCT ID init_declarator_list SEMI + | ( ID init_declarator_list ) => ID init_declarator_list SEMI + | statement + ; + +init_declarator_list + : init_declarator ( COMMA init_declarator )* ; + +init_declarator + : declarator ( ASSIGN initializer )? ; + +declarator + : ID ( LBRACKET ( constant_expression )? RBRACKET )*; + +struct_definition + : STRUCT ( ID )? LCURLY struct_declaration_list RCURLY ID? SEMI; + +struct_declaration_list + // We currently don't support nested structs so the field type + // can only be either a scalar or a vector. + : ( struct_interpolation_modifier? + (scalar_type_specifier|vector_type_specifier) ID + (COLON semantic_specifier)? SEMI )+ + ; + +struct_interpolation_modifier // DX10 only + : 'linear' + | 'centroid' + | 'nointerpolation' + | 'noperspective' + ; + +semantic_specifier + : ID ; + +statement + : ( lvalue_expression assignment_operator ) => assignment_statement + | ( lvalue_expression self_modify_operator ) => post_modify_statement + | pre_modify_statement + | expression_statement + | compound_statement + | selection_statement + | iteration_statement + | jump_statement + | SEMI + ; + +assignment_statement + : lvalue_expression assignment_operator expression SEMI ; + +pre_modify_statement + : pre_modify_expression SEMI ; + +pre_modify_expression + : self_modify_operator lvalue_expression ; + +post_modify_statement + : post_modify_expression SEMI ; + +post_modify_expression + : lvalue_expression self_modify_operator ; + +self_modify_operator + : PLUSPLUS | MINUSMINUS ; + +expression_statement + : expression SEMI ; + +compound_statement + : LCURLY ( + ( ID init_declarator_list) => ID init_declarator_list SEMI + | ( ( CONST )? vector_type_specifier ) => ( CONST )? vector_type_specifier init_declarator_list SEMI + | ( ( CONST )? scalar_type_specifier ) => ( CONST )? scalar_type_specifier init_declarator_list SEMI + | ( STRUCT ( ID )? LCURLY ) => struct_definition ( init_declarator_list )? SEMI + | STRUCT ID init_declarator_list SEMI + | statement + )* + RCURLY + ; + +selection_statement + : IF LPAREN expression RPAREN statement ( ELSE statement )? + ; + +iteration_statement + : WHILE LPAREN expression RPAREN statement + | FOR LPAREN assignment_statement + equality_expression SEMI modify_expression RPAREN statement + | DO statement WHILE LPAREN expression RPAREN SEMI + ; + +modify_expression + : (lvalue_expression assignment_operator ) => + lvalue_expression assignment_operator expression + | pre_modify_expression + | post_modify_expression + ; + +jump_statement + : BREAK SEMI + | CONTINUE + | RETURN ( expression )? SEMI + | DISCARD + ; + +expression + : conditional_expression ; + +assignment_operator + : ASSIGN + | MUL_ASSIGN + | DIV_ASSIGN + | ADD_ASSIGN + | SUB_ASSIGN + | BITWISE_AND_ASSIGN + | BITWISE_OR_ASSIGN + | BITWISE_XOR_ASSIGN + | BITWISE_SHIFTL_ASSIGN + | BITWISE_SHIFTR_ASSIGN + ; + +constant_expression + : (ID) => variable_expression + | literal_value ; + +conditional_expression + : logical_or_expression ( QUESTION expression COLON conditional_expression )? + ; + +logical_or_expression + : exclusive_or_expression ( OR exclusive_or_expression )* + ; + +// We remove the NOT operator from the unary expression and stick it here +// so that it has a lower precedence than relational operations. +logical_and_expression + : ( NOT )? inclusive_or_expression ( AND ( NOT )? inclusive_or_expression )* + ; + +inclusive_or_expression + : exclusive_or_expression (BITWISE_OR exclusive_or_expression )* + ; + +exclusive_or_expression + : and_expression ( BITWISE_XOR and_expression )* + ; + +and_expression + : equality_expression ( BITWISE_AND equality_expression )* + ; + +equality_expression + : relational_expression ( (EQUAL|NOT_EQUAL) relational_expression )* + ; + +relational_expression + : shift_expression ( (LT|GT|LTE|GTE) shift_expression )* + ; + +shift_expression + : additive_expression ( (BITWISE_SHIFTL|BITWISE_SHIFTR) additive_expression )* + ; + +additive_expression + : multiplicative_expression ( (PLUS|MINUS) multiplicative_expression )* + ; + +multiplicative_expression + : cast_expression ( (MUL|DIV|MOD) cast_expression )* + ; + +cast_expression + : LBRACKET param_type_specifier RBRACKET cast_expression + | unary_expression + ; + +unary_expression + : (PLUS|MINUS) unary_expression + | postfix_expression + ; + +postfix_expression + : primary_expression ( postfix_suffix )? ; + +lvalue_expression + : variable_expression ( postfix_suffix )? ; + +postfix_suffix + // choosing between struct field access or vector swizzling is a semantic choice. + : ( DOT swizzle ) + | ( DOT primary_expression )+ ; + +primary_expression +// : constructor + : intrinsic_name LPAREN argument_expression_list RPAREN + | variable_or_call_expression + | literal_value + | LPAREN expression RPAREN + ; + +variable_expression + : ID ( LBRACKET expression RBRACKET )? ; + +// Combine variable expression and call expression here to get rid of the +// syntactic predicate we used to use in the primary_expression rule. Using +// predicates results in the parser spending a lot of time in exception +// handling (when lookahead fails). +variable_or_call_expression + : ID + ( + ( ( LBRACKET expression RBRACKET )? ) + | + ( LPAREN argument_expression_list RPAREN ) + ) + ; + +constructor + : vector_type_specifier LPAREN expression ( COMMA expression )* RPAREN ; + +argument_expression_list + : ( expression ( COMMA expression )* )? ; + +intrinsic_name + : ABS + | ACOS + | ALL + | ANY + | ASFLOAT + | ASIN + | ASINT + | ASUINT + | ATAN + | ATAN2 + | CEIL + | CLAMP + | CLIP + | COS + | COSH + | CROSS + | DDX + | DDY + | DEGREES + | DETERMINANT + | DISTANCE + | DOT + | EXP + | EXP2 + | FACEFORWARD + | FLOOR + | FMOD + | FRAC + | FREXP + | FWIDTH + | ISFINITE + | ISINF + | ISNAN + | LDEXP + | LENGTH + | LERP + | LIT + | LOG + | LOG10 + | LOG2 + | MAX + | MIN + | MODF + | MUL + | NOISE + | NORMALIZE + | POW + | RADIANS + | REFLECT + | REFRACT + | ROUND + | RSQRT + | SATURATE + | SIGN + | SIN + | SINCOS + | SINH + | SMOOTHSTEP + | SQRT + | STEP + | TAN + | TANH + | TEX1D + | TEX1DBIAS + | TEX1DGRAD + | TEX1DLOD + | TEX1DPROJ + | TEX2D + | TEX2DBIAS + | TEX2DGRAD + | TEX2DLOD + | TEX2DPROJ + | TEX3D + | TEX3DBIAS + | TEX3DGRAD + | TEX3DLOD + | TEX3DPROJ + | TEXCUBE + | TEXCUBEBIAS + | TEXCUBEGRAD + | TEXCUBELOD + | TEXCUBEPROJ + | TRANSPOSE + | TRUNC + ; + +int_literal + : DECIMAL_LITERAL + | OCT_LITERAL + | HEX_LITERAL + ; +literal_value + : ('"') => string_literal + | int_literal + | float_literal + ; + + +float_literal + : FLOAT_LITERAL + ; + +string_literal + : '"' STRING_LITERAL '"' + ; + +vector_subcomponent + : DOT ('x'|'y'|'z'|'w'|'r'|'g'|'b'|'a') + ; + +swizzle + : ( s += ('x'|'y'|'z'|'w') )+ { $s.size() <= 4 }? + | ( s += ('r'|'g'|'b'|'a') )+ { $s.size() <= 4 }? + ; + +// effects --------------------------------------- + +technique + : 'technique' ID? '{' (pass)+ '}' SEMI? + ; + +pass + : 'pass' (ID)? '{' (state_assignment)* '}' SEMI? + ; + +state_assignment + : a=ID '=' state_assignment_value SEMI + { System.out.println("Found stateassignment: " + $a.text); } + ; + +state_assignment_value + : 'compile' p=ID q=variable_or_call_expression + { System.out.println("Found compile: " + $p.text + " " + $q.text); } + | a=expression + { System.out.println("Found stateassignmentvalue: " + $a.text); } + | ('true' | 'false') + ; + +// ----------------------------------------------------------------------------- + +NOT : '!' ; +NOT_EQUAL : '!=' ; +AND : '&&' ; +LPAREN : '(' ; +RPAREN : ')' ; +MUL : '*' ; +MUL_ASSIGN : '*=' ; +PLUS : '+' ; +PLUSPLUS : '++' ; +ADD_ASSIGN : '+=' ; +COMMA : ',' ; +MINUS : '-' ; +MINUSMINUS : '--' ; +SUB_ASSIGN : '-=' ; +DIV : '/' ; +DIV_ASSIGN : '/=' ; +MOD : '%'; +MOD_ASSIGN : '%='; +COLON : ':' ; +SEMI : ';' ; +LT : '<' ; +LTE : '<=' ; +ASSIGN : '=' ; +EQUAL : '==' ; +GT : '>' ; +GTE : '>=' ; +QUESTION : '?' ; +LBRACKET : '[' ; +RBRACKET : ']' ; +LCURLY : '{' ; +OR : '||' ; +RCURLY : '}' ; +DOT : '.' ; +BITWISE_NOT : '~'; +BITWISE_SHIFTL : '<<'; +BITWISE_SHIFTR : '>>'; +BITWISE_AND : '&'; +BITWISE_OR : '|'; +BITWISE_XOR : '^'; +BITWISE_SHIFTL_ASSIGN : '<<='; +BITWISE_SHIFTR_ASSIGN : '>>='; +BITWISE_AND_ASSIGN : '&='; +BITWISE_OR_ASSIGN : '|='; +BITWISE_XOR_ASSIGN : '^='; + +// keywords ---------------------------- + +BREAK : 'break'; +BUFFER : 'buffer'; +CBUFFER : 'cbuffer'; +CONST : 'const'; +CONTINUE : 'continue'; +DISCARD : 'discard'; +DO : 'do'; +ELSE : 'else'; +EXTERN : 'extern'; +FALSE : 'false'; +FOR : 'for'; +IF : 'if'; +IN : 'in'; +INLINE : 'inline'; +INOUT : 'inout'; +MATRIX : 'matrix'; +NAMESPACE : 'namespace'; +NOINTERPOLATION : 'nointerpolation'; +OUT : 'out'; +RETURN : 'return'; +REGISTER : 'register'; +SHARED : 'shared'; +STATEBLOCK : 'stateblock'; +STATEBLOCK_STATE : 'stateblock_state'; +STATIC : 'static'; +STRING : 'string'; +STRUCT : 'struct'; +SWITCH : 'switch'; +TBUFFER : 'tbuffer'; +TEXTURE : 'texture'; +TEXTURE1D : 'texture1d'; +TEXTURE1DARRAY : 'texture1darray'; +TEXTURE2D : 'texture2d'; +TEXTURE2DARRAY : 'texture2darray'; +TEXTURE2DMS : 'texture2dms'; +TEXTURE2DMSARRAY : 'texture2dmsarray'; +TEXTURE3D : 'texture3d'; +TEXTURECUBE : 'texturecube'; +TEXTURECUBEARRAY : 'texturecubearray'; +TRUE : 'true'; +TYPEDEF : 'typedef'; +UNIFORM : 'uniform'; +VOID : 'void'; +VOLATILE : 'volatile'; +WHILE : 'while'; + +// fx keywords --------------------- + +BLENDSTATE : 'blendstate'; +COMPILE : 'compile'; +DEPTHSTENCILSTATE : 'depthstencilstate'; +DEPTHSTENCILVIEW : 'depthstencilview'; +GEOMETRYSHADER : 'geometryshader'; +PASS : 'pass'; +PIXELSHADER : 'pixelshader'; +RASTERIZERSTATE : 'rasterizerstate'; +RENDERTARGETVIEW : 'rendertargetview'; +TECHNIQUE : 'technique'; +TECHNIQUE10 : 'technique10'; +VERTEXSHADER : 'vertexshader'; + + +// intrinsic functions -------------------- +ABS : 'abs'; +ACOS : 'acos'; +ALL : 'all'; +ANY : 'any'; +ASFLOAT : 'asfloat'; +ASIN : 'asin'; +ASINT : 'asint'; +ASUINT : 'asuint'; +ATAN : 'atan'; +ATAN2 : 'atan2'; +CEIL : 'ceil'; +CLAMP : 'clamp'; +CLIP : 'clip'; +COS : 'cos'; +COSH : 'cosh'; +CROSS : 'cross'; +DDX : 'ddx'; +DDY : 'ddy'; +DEGREES : 'degrees'; +DETERMINANT : 'determinant'; +DISTANCE : 'distance'; +DOT_PRODUCT : 'dot'; +EXP : 'exp'; +EXP2 : 'exp2'; +FACEFORWARD : 'faceforward'; +FLOOR : 'floor'; +FMOD : 'fmod'; +FRAC : 'frac'; +FREXP : 'frexp'; +FWIDTH : 'fwidth'; +ISFINITE : 'isfinite'; +ISINF : 'isinf'; +ISNAN : 'isnan'; +LDEXP : 'ldexp'; +LENGTH : 'length'; +LERP : 'lerp'; +LIT : 'lit'; +LOG : 'log'; +LOG10 : 'log10'; +LOG2 : 'log2'; +MAX : 'max'; +MIN : 'min'; +MODF : 'modf'; +MUL_FUNC : 'mul'; +NOISE : 'noise'; +NORMALIZE : 'normalize'; +POW : 'pow'; +RADIANS : 'radians'; +REFLECT : 'reflect'; +REFRACT : 'refract'; +ROUND : 'round'; +RSQRT : 'rsqrt'; +SATURATE : 'saturate'; +SIGN : 'sign'; +SIN : 'sin'; +SINCOS : 'sincos'; +SINH : 'sinh'; +SMOOTHSTEP : 'smoothstep'; +SQRT : 'sqrt'; +STEP : 'step'; +TAN : 'tan'; +TANH : 'tanh'; +TEX1D : 'tex1D'; +TEX1DBIAS : 'tex1Dbias'; +TEX1DGRAD : 'tex1Dgrad'; +TEX1DLOD : 'tex1Dlod'; +TEX1DPROJ : 'tex1Dproj'; +TEX2D : 'tex2D'; +TEX2DBIAS : 'tex2Dbias'; +TEX2DGRAD : 'tex2Dgrad'; +TEX2DLOD : 'tex2Dlod'; +TEX2DPROJ : 'tex2Dproj'; +TEX3D : 'tex3D'; +TEX3DBIAS : 'tex3Dbias'; +TEX3DGRAD : 'tex3Dgrad'; +TEX3DLOD : 'tex3Dlod'; +TEX3DPROJ : 'tex3Dproj'; +TEXCUBE : 'texCUBE'; +TEXCUBEBIAS : 'texCUBEbias'; +TEXCUBEGRAD : 'texCUBEgrad'; +TEXCUBELOD : 'texCUBElod'; +TEXCUBEPROJ : 'texCUBEproj'; +TRANSPOSE : 'transpose'; +TRUNC : 'trunc'; + + + +// fundamental tokens --------------------- + +fragment HEXDIGIT + : ('0'..'9'|'a'..'f'|'A'..'F') + ; + +fragment UNICODE_ESCAPE + : '\\' 'u' HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT + ; + +fragment OCTAL_ESCAPE + : '\\' ('0'..'3') ('0'..'7') ('0'..'7') + | '\\' ('0'..'7') ('0'..'7') + | '\\' ('0'..'7') + ; + +fragment ESCAPE_SEQUENCE + : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\') + | UNICODE_ESCAPE + | OCTAL_ESCAPE + ; + +fragment EXPONENT : ('e'|'E') (PLUS | MINUS)? ('0'..'9')+ ; + +fragment FLOATSUFFIX : ('f'|'F'|'h'|'H') ; + +ID + : ('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')* + { + // check the length of the identifier before accepting it. + if (($ID.length() > 96)) { + RecognitionException(); + } + } + ; + +DECIMAL_LITERAL + : ('1'..'9')('0'..'9')+ + ; + +OCT_LITERAL + : ('0'..'3') ('0'..'7') ('0'..'7') + | ('0'..'7') ('0'..'7') + | ('0'..'7') + ; + +HEX_LITERAL + : '0x' HEXDIGIT+ + ; + +CHARACTER_LITERAL + : '\'' ( ESCAPE_SEQUENCE | ~('\''|'\\') ) '\'' + ; + +STRING_LITERAL + : ( ESCAPE_SEQUENCE | ~('\\'|'"') )* + ; + +FLOAT_LITERAL + : (PLUS | MINUS)? ('0'..'9')+ '.' ('0'..'9')* (EXPONENT)? (FLOATSUFFIX)? + | (PLUS | MINUS)? '.' ('0'..'9')+ (EXPONENT)? (FLOATSUFFIX)? + | (PLUS | MINUS)? ('0'..'9')+ (EXPONENT)? (FLOATSUFFIX)? + ; + +// skipped elements ----------------- + +WHITESPACE + : ( ' ' | '\t' | '\f' | '\r' ) + { $channel = HIDDEN; } + ; + +COMMENT + : '//' (~('\n'|'\r'))* + { $channel = HIDDEN; } + ; + +MULTILINE_COMMENT + : '/*' ( options {greedy=false;} : . )* '*/' + { $channel = HIDDEN; } + ; + +//RESERVED_WORDS +// : 'auto' +// | 'case' +// | 'catch' +// | 'char' +// | 'class' +// | 'const_cast' +// | 'default' +// | 'delete' +// | 'dynamic_cast' +// | 'enum' +// | 'explicit' +// | 'friend' +// | 'goto' +// | 'long' +// | 'mutable' +// | 'new' +// | 'operator' +// | 'private' +// | 'protected' +// | 'public' +// | 'reinterpret_cast' +// | 'short' +// | 'signed' +// | 'sizeof' +// | 'snorm' +// | 'static_cast' +// | 'template' +// | 'this' +// | 'throw' +// | 'try' +// | 'typename' +// | 'union' +// | 'unorm' +// | 'unsigned' +// | 'using' +// | 'varying' +// | 'virtual' +// { $channel = HIDDEN; } +// ; diff --git a/o3d/compiler/puritan/build.scons b/o3d/compiler/puritan/build.scons new file mode 100644 index 0000000..7c59d0d --- /dev/null +++ b/o3d/compiler/puritan/build.scons @@ -0,0 +1,57 @@ +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# Import the build environment that was decided in the project SConstruct. +# This may be the Debug or Optimized environment +Import('env') + +# Create a list of the files used to build the target library. +inputs = [ +'exp_gen.cc', +'knobs.cc', +'puritan.cc', +'puritan_assert.cc', +'rand.cc', +'structure_gen.cc', +'test_gen.cc', +'main.cc', +] + +libraries = [] + +# On Windows link with ADVAPI32 to get better RNG. +if env.Bit('windows'): + libraries += ['advapi32.lib'] + +# Create the puritan tool for generating random HLSL shaders +puritan = env.Program('puritan', inputs, LIBS=libraries) +puritan_install = env.Replicate('$ARTIFACTS_DIR', puritan) + +env.Alias('puritan', puritan_install) diff --git a/o3d/compiler/puritan/exp_gen.cc b/o3d/compiler/puritan/exp_gen.cc new file mode 100644 index 0000000..29c126b --- /dev/null +++ b/o3d/compiler/puritan/exp_gen.cc @@ -0,0 +1,934 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <iostream> +#include "puritan_assert.h" +#include "exp_gen.h" +#include "structure_gen.h" +#include "knobs.h" + +namespace Salem +{ +namespace Puritan +{ + + +const char *f2tof_swizzle_names[] = +{ + "x", "y", 0 +}; + +StrCoverage f2tof_swizzles ("F2tof Swizzles", + f2tof_swizzle_names); + +const char *f2tof2_swizzle_names[] = +{ + "xx", "yy", "xy", "yx", 0 +}; + +StrCoverage f2tof2_swizzles ("F2tof2 Swizzles", + f2tof2_swizzle_names); + +const char *f4tof_swizzle_names[] = +{ + "x", "y", "z", "w", 0 +}; + +StrCoverage f4tof_swizzles ("F4tof Swizzles", + f4tof_swizzle_names); + +const char *f4tof2_swizzle_names[] = +{ + "xx", "xy", "xw", "xz", + "yy", "yx", "yw", "yz", + "wx", "wy", "ww", "wz", + "zx", "zy", "zw", "zz", + 0 +}; + +StrCoverage f4tof2_swizzles ("F4tof2 Swizzles", + f4tof2_swizzle_names); + +const char *f4tof4_swizzle_names[] = +{ + "x", "y", "z", "w", 0 +}; + +StrCoverage f4tof4_swizzles ("f4tof4 Swizzles", + f4tof4_swizzle_names); + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +// This code generates controlled random expressions for Puritan. + +// During generation, various knobs and random numbers determine the complexity +// of the expression and push various operands into a list. Then the code +// builds an expression tree removing things from the list until there's +// nothing left. +// + +////////////////////////////////////////////////////////////////////// + +// "frac" removed because it can cause discrepant CPU vs GPU results +// - see ticket #3493 +const char *unfunc_names[] = +{ "abs", /* "frac", */ "exp2", "log2", + "rcp", "rsqrt", "sqrt", "!", 0 +}; + +StrCoverage unfuncs ("Unary functions", unfunc_names); + +const char *binfunc_names[] = +{ "max", "min", "dot", 0 }; + +StrCoverage binfuncs ("Binary functions", binfunc_names); + +const char *trifunc_names[] = +{ "mad", "opcond", 0}; + +StrCoverage trifuncs ("Trinary functions", trifunc_names); + +const char *unop_names[] = +{ "-", 0 }; + +StrCoverage unops ("Unops", unop_names); + +const char *binop_names[] = +{ "*", "/", "+", "-", 0 }; + +StrCoverage binops ("Binops", binop_names); + +// "eq", "ne" removed because these are very sensitive to precision errors +// and could cause discrepant CPU vs GPU results +const char *relop_names[] = +{ + "lt", "le", "gt", "ge", + /* "eq", "ne", */ "and", "or", + 0 +}; + +StrCoverage relops ("Relops", relop_names); + +const char *special_names[] = +{ + "* 2.0", "* 4.0", "* 8.0", + "/ 2.0", "/ 4.0", "/ 8.0", + "1.0 -", "1.0-2.0*", 0 +}; + +StrCoverage special ("Special", special_names); + +std::ostream & operator << (std::ostream & out, + const ENode & x) +{ + x->print (out); + return out; +} + +void print_e (const ENode &x) +{ + std::cerr << x; +} + +std::ostream & operator << (std::ostream & out, + const EList & x) +{ + bool need_comma = false; + + for (EList::const_iterator i = x.begin (); i != x.end (); + i++) + { + if (need_comma) + { + out << ", "; + } + out << *i; + need_comma = true; + } + return out; +} + +////////////////////////////////////////////////////////////////////// +// Generate expressions.. + +// Remove one operand from the stack of expressions and convert it to the +// prevailing type. + +ENode one_arg (exp_list * exp, Rand * rand, const Type & type, Gen *gen) +{ + PURITAN_ASSERT (!exp->empty (), "Ran out of expressions"); + ENode arg = exp->back (); + + exp->pop_back (); + + return ENode (Exp::convert (arg, rand, type, gen)); +} + +// Create an expression tree returning with type RETURN_TYPE. +ENode create_expression (Gen * gen, Type return_type, const Context & ctx) +{ + Rand *rand = &gen->rand; + static int depth; + Type type = return_type; + const Knobs & knobs = gen->knobs; + exp_list exp; + + depth++; + + // We create some things regardless of the complexity settings. + if (return_type == Float4 && knobs.float4_chance (rand)) + { + // Make more args - call create_expression out of the arg list + // because otherwise the expression eval order will be undefined. + ENode a0 = (create_expression (gen, Float, ctx.deeper ())); + ENode a1 = (create_expression (gen, Float, ctx.deeper ())); + ENode a2 = (create_expression (gen, Float, ctx.deeper ())); + ENode a3 = (create_expression (gen, Float, ctx.deeper ())); + + exp.push_back (ENode (new Float4Func (Float4, a0, a1, a2, a3))); + } + + if (return_type == Float2 && knobs.float2_chance (rand)) + { + ENode a0 = (create_expression (gen, Float, ctx.deeper ())); + ENode a1 = (create_expression (gen, Float, ctx.deeper ())); + + exp.push_back (ENode (new BiIntrinsicFunc (Float2, "float2", a0, a1))); + } + + if (ctx.callees && !ctx.callees->empty ()) + { + FunctionSPtr target = ctx.callees->front (); + EList actuals; + + ctx.callees->pop_front (); + + for (DeclList::iterator ai = target->formals.begin (); + ai != target->formals.end (); + ai++) + { + switch (ai->scope.scope) + { + // input argument can be any expression + case Argument_IO: + case Argument_I: + { + ENode actual = create_expression (gen, ai->type, ctx.deeper ()); + actuals.push_back (actual); + break; + } + case Argument_O: + { + ENode actual (new Variable + (gen->fetch_decl (ai->type, + ctx.deeper (), + Uninitialized, + Read))); + + actuals.push_back (actual); + break; + } + + default: + case Uniform: + case Static: + case StaticConstArrays: + case Sampler: + case NoScope: + { + PURITAN_ABORT ("Bad scope" << ai->scope.scope); + break; + } + } + } + + exp.push_back + (ENode (new FCallTemplate (target->ret_type, target, actuals))); + } + + if (ctx.samplers && !ctx.samplers->empty ()) + { + // Have to get the arguments in range + // tex2D (id<n>, <exp> % id<n>_size) + unsigned sampler = ctx.samplers->front (); + + ctx.samplers->pop_front (); + ENode lhs = create_expression (gen, Float4, ctx.deeper ()); + ENode rhs (new SamplerSizeRef (sampler)); + ENode mod (new BiIntrinsicFunc (Float4, "quick_mod", lhs, rhs)); + + exp.push_back (ENode (new SamplerRef (sampler, mod))); + } + + // Build as many terms as we were asked for. + int l = gen->knobs.expression_depth (rand); + + // Children functions get fewer nodes. + if (ctx.func) + { + l = l * 4 / 6 + 1; + } + + for (int i = 0; i < l; i++) + { + if (knobs.type_change_chance (rand)) + { + type = gen->random_type (); + } + + if (knobs.array_use (rand)) + { + Type t = knobs.array_constness (rand) + ? Float4ConstArray : Float4Array; + + Decl decl = gen->gen_array_decl (t, ctx.deeper (), Read); + ENode v (new Variable (decl)); + ENode idx; + + if (knobs.array_index_const (rand) || ctx.loop.get() == NULL) + { + idx = ENode (new Constant (Float, + rand->range (0, array_size))); + } + else + { + idx = ENode (new Variable (ctx.loop->counter)); + } + + ENode id (new Index (v, idx)); + exp.push_back (id); + } + else + { + if (knobs.uniform_chance (rand) && gen->n_uniforms) + { + size_t uni_idx = rand->srange (0, gen->n_uniforms); + std::vector <std::pair <Type, string> >::iterator uni + = gen->uniforms.begin () + uni_idx; + ENode e (new UniformRef (uni->first, uni->second)); + exp.push_back (e); + } + else if (ctx.depth > 10 || knobs.constant_use (rand)) + { + ENode e (new Constant (Float, gen->gen_fconstant ())); + exp.push_back (e); + } + else + { + Type t + = knobs.type_change_chance (rand) + ? gen->random_type () : type; + + Decl decl = gen->fetch_decl (t, + ctx.deeper (), + Initialized, + Read); + ENode v (new Variable (decl)); + + exp.push_back (v); + } + } + + // Stop making nodes if we've gone too far. + gen->exp_nodes++; + if (gen->exp_nodes >= knobs.exp_limit.uget ()) + { + break; + } + } + + // Now add enough operators to consume all but one of the terms, the final + // term is the result of the expression. + + bool no_more_unarys = false; + + while (exp.size () != 1) + { + if ((type == Float + && knobs.relop_chance (rand)) + || (ctx.relop_p + && exp.size () < 3 && knobs.relop_cond_chance (rand))) + { + exp.push_back + (ENode (new Relop (relops.choose (rand), + one_arg (&exp, rand, Float, gen), + one_arg (&exp, rand, Float, gen)))); + + } + else if (knobs.special_chance (rand)) + { + exp.push_back + (ENode (new SpecialPhrase (type, + special.choose (rand), + one_arg (&exp, rand, type, gen)))); + } + else if (knobs.func_chance (rand)) + { + if (exp.size () > 2 && knobs.trifunc_chance (rand)) + { + string name = trifuncs.choose (rand); + + + // name of opcond function changes by type + if (name == "opcond") + { + switch (type.type) + { + case Float4: + name = "opcond4"; + break; + case Float2: + name = "opcond2"; + break; + case Float: + name = "opcond"; + break; + case NoType: + case Int: + case Int4: + case SamplerFloat4: + case SamplerSize: + case Struct: + case Float4ConstArray: + case Float4Array: + default: + PURITAN_ABORT ("Illegal type for opcond" << type.type) ; + break; + } + } + + exp.push_back + (ENode (new TriIntrinsicFunc (type, + name, + one_arg (&exp, rand, type, gen), + one_arg (&exp, rand, type, gen), + one_arg (&exp, rand, type, gen)))); + } + else if (exp.size () > 1 && knobs.binfunc_chance (rand)) + { + string f = binfuncs.choose (rand); + Type ret_type = f == "dot" ? Float : type; + + ENode lhs = one_arg (&exp, rand, type, gen); + ENode rhs = one_arg (&exp, rand, type, gen); + exp.push_back + (ENode (new BiIntrinsicFunc (ret_type, + f, + lhs, + rhs))); + type = ret_type; + } + else + { + exp.push_back + (ENode (new UnIntrinsicFunc (type, + unfuncs.choose (rand), + one_arg (&exp, rand, type, gen)))); + } + } + else if (!no_more_unarys && knobs.unary_chance (rand)) + { + exp.push_back (ENode (new Unop (type, + unops.choose (rand), + one_arg (&exp, rand, type, gen)))); + } + else if (knobs.swizzle_chance (rand)) + { + exp.push_back (Exp::convert (one_arg (&exp, rand, type, gen), rand, type, gen)); + } + else + { + exp.push_back + (ENode (new Binop (type, binops.choose (rand), + one_arg (&exp, rand, type, gen), + one_arg (&exp, rand, type, gen)))); + } + } + + return Exp::convert (exp.back (), rand, return_type, gen); + +} +////////////////////////////////////////////////////////////////////// +/// ENode Template, base class for all expression nodes. + +Exp::Exp (Type _type) :type (_type) +{ +} + +Exp::~ Exp () +{ +} + +bool Exp::is_fconstant () const +{ + return false; +} + +// Convert a tree from one type to another, return the new tree. +ENode Exp::convert (ENode from, Rand * rand, Type to, Gen *gen) +{ + string pre; + string post; + unsigned n = 1; + + if (from->type == to.type && !gen->knobs.copy_swizzle_chance (rand)) + { + n = 0; + } + else if (from->type == Float && to.type == Float) + { + + } + else if (from->type == Float2 && to.type == Float2) + { + post = f2tof2_swizzles.choose (rand); + } + else if (from->type == Float && to.type == Float2) + { + n = 1; + pre = "float2"; + } + else if (from->type == Float && to.type == Float4) + { + n = 1; + pre = "float4"; + } + else if (from->type == Float2 && to.type == Float) + { + post = f2tof_swizzles.choose (rand); + } + else if (from->type == Float2 && to.type == Float4) + { + // Can't spread out a float2 + pre = "float4"; + n = 2; + } + else if (from->type == Float4 && to.type == Float) + { + post = f4tof_swizzles.choose (rand); + } + else if (from->type == Float4 && to.type == Float2) + { + post = f4tof2_swizzles.choose (rand); + } + else if (from->type == Float4 && to.type == Float4) + { + for (unsigned i = 0; i < 4; i++) + post += f4tof4_swizzles.choose (rand); + } + else + { + PURITAN_ABORT ("Bad args in convert"); + } + + if (from->is_fconstant ()) + { + n = 1; + } + return ENode (new Convert (to, from->type, pre, post, n, from)); +} + +////////////////////////////////////////////////////////////////////// +/// Boilerplate + +A0::A0 (Type ty) :Exp (ty) +{ +} + +A1::A1 (Type ty, ENode _child) :Exp (ty), child (_child) +{ +} + +A2::A2 (Type ty, ENode _lhs, ENode _rhs) :Exp (ty), + lhs (_lhs), rhs (_rhs) +{ +} + +A3::A3 (Type ty, ENode _a0, ENode _a1, + ENode _a2) :Exp (ty), a0 (_a0), a1 (_a1), + a2 (_a2) +{ +} + +A4::A4 (Type ty, ENode _a0, ENode _a1, ENode _a2, + ENode _a3) :Exp (ty), a0 (_a0), a1 (_a1), + a2 (_a2), a3 (_a3) +{ +} + +////////////////////////////////////////////////////////////////////// +/// Unary operations + +Unop::Unop (Type ty, string _name, ENode _child) + :A1 (ty, _child), name (_name) +{ +} + +void Unop::print (ostream & out) const +{ + out << " ( " << name << " " << child << ") "; +} + +////////////////////////////////////////////////////////////////////// +/// Function calls. + +FCallTemplate::FCallTemplate (TypeList ty, + FunctionSPtr _target, + EList _actuals) + :Exp (ty.front ()), target (_target), actuals (_actuals) +{ +} + +void FCallTemplate::print (ostream & out) const +{ + out << "func" << target->idx << " (" << actuals << ")"; +} + +////////////////////////////////////////////////////////////////////// +/// Sampler references. + +SamplerRef::SamplerRef (unsigned _n, ENode _child) + :A1 (Float4, _child), n (_n) +{ +} + +void SamplerRef::print (ostream & out) const +{ + out << "tex2D (in" << n << ", " << child << ")"; +} + +////////////////////////////////////////////////////////////////////// +/// Sampler references. + +SamplerSizeRef::SamplerSizeRef (unsigned _n) + :A0 (Float4), n (_n) +{ +} + +void SamplerSizeRef::print (ostream & out) const +{ + out << "in" << n << "_size"; +} + +////////////////////////////////////////////////////////////////////// +/// Uniform references. + +UniformRef::UniformRef (Type ty, string v) + :A0 (ty), name (v) +{ +} + +void UniformRef::print (ostream & out) const +{ + out << name; +} + +////////////////////////////////////////////////////////////////////// +/// Constants. + +Constant::Constant (Type ty, string _val) + :A0 (ty), val (_val) +{ +} + +void Constant::print (ostream & out) const +{ + out << val; +} + +Constant::Constant (Type ty, unsigned _val) + :A0 (ty) +{ + ostringstream o; + + o << _val; + val = (o.str ()); +} +bool Constant::is_fconstant () const +{ + return true; +} + +////////////////////////////////////////////////////////////////////// +/// Variables. + +Variable::Variable (Decl _d) + :A0 (_d.type ()), decl (_d) +{ +} + +void Variable::print (ostream & out) const +{ + out << " " << decl; +} + +////////////////////////////////////////////////////////////////////// +/// Indexes into arrays. + +Index::Index (ENode child, ENode idx) + :A2 (Float4, child, idx) +{ +} + +void Index::print (ostream & out) const +{ + out << lhs << "[" << rhs << "]"; +} + +////////////////////////////////////////////////////////////////////// +/// Constant array references. + +ConstArrayRef::ConstArrayRef (Decl _d) + :A0 (Float4), decl (_d) +{ +} + +void ConstArrayRef::print (ostream & out) const +{ + out << " " << decl; +} + +////////////////////////////////////////////////////////////////////// +/// lhs swizzles + +Swizzle::Swizzle (Type ty, string _name, ENode _child) + : A1 (ty, _child), name (_name) +{ + +} + +void Swizzle::print (ostream & out) const +{ + out << child << "." << name; +} + +////////////////////////////////////////////////////////////////////// +/// Variables on the LHS of an assignment. + +LHSVariable::LHSVariable (Decl _d, bool _swizzled) + :Exp (_d.type ()), decl (_d), swizzled (_swizzled) +{ +} + +void LHSVariable::print (ostream & out) const +{ + out << " " << decl; +} + +////////////////////////////////////////////////////////////////////// +/// Special phrases. + +SpecialPhrase::SpecialPhrase (Type t, string _name, ENode _child) + :A1 (t, _child), name (_name) +{ +} + +void SpecialPhrase::print (ostream & out) const +{ + if (name[0] == '1') + { + out << " " << name << child; + } + else + { + out << child << " " + name; + } +} + +////////////////////////////////////////////////////////////////////// +/// Unary intrinsic + +UnIntrinsicFunc::UnIntrinsicFunc (Type ty, string _name, + ENode _child) + :A1 (ty, _child), + name (_name) +{ +} + +void UnIntrinsicFunc::print (ostream & out) const +{ + if (name == "!") + { + out << "(!(" << child << "))"; + } + else + { + out << " " << name << " (" << child << ")"; + } +} + +////////////////////////////////////////////////////////////////////// +/// Binary intrinsic + +BiIntrinsicFunc::BiIntrinsicFunc (Type ty, string _name, + ENode _lhs, ENode _rhs) + :A2 (ty, _lhs, _rhs), name (_name) +{ +} + +void BiIntrinsicFunc::print (ostream & out) const +{ + out << " " << name << " (" << lhs << ", " << rhs << ")"; +} + +////////////////////////////////////////////////////////////////////// +/// Tri intrinsic + +TriIntrinsicFunc::TriIntrinsicFunc (Type ty, + string _name, + ENode _a1, + ENode _a2, + ENode _a3) + :A3 (ty, _a1, _a2, _a3), name (_name) +{ +} + +void TriIntrinsicFunc::print (ostream & out) const +{ + out << name << "(" << a0 << ", " << a1 << ", " << a2 << ")"; +} + +////////////////////////////////////////////////////////////////////// +/// Float4 + +Float4Func::Float4Func (Type ty, ENode _a1, ENode _a2, + ENode _a3, ENode _a4) + :A4 (ty, _a1, _a2, _a3, _a4) +{ +} + +void Float4Func::print (ostream & out) const +{ + out << "float4 (" << a0 << ", " << a1 << ", " << a2 << ", " << a3 + << ")"; +} + +////////////////////////////////////////////////////////////////////// +/// Convert + +Convert::Convert (Type to, + Type _from, + string _pre, string _post, unsigned _n, + ENode _child) + :A1 (to, _child), from (_from), pre (_pre), post (_post), n (_n) +{ +} + +void Convert::print (ostream & out) const +{ + if (n == 0) + { + out << child; + } + else + { + out << pre << " ("; + for (unsigned i = 0; i < n; i++) + { + if (i) + { + out << ", "; + } + out << child; + } + out << ")"; + if (!post.empty ()) + { + out << "." << post; + } + } +} + +////////////////////////////////////////////////////////////////////// +/// Binop + +Binop::Binop (Type ty, string _name, ENode _lhs, + ENode _rhs) + :A2 (ty, _lhs, _rhs), + name (_name) +{ +} + +void Binop::print (ostream & out) const +{ + out << lhs << " " << name << " " << rhs; +} + +////////////////////////////////////////////////////////////////////// +/// Relop + +Relop::Relop (string _name, ENode _lhs, ENode _rhs) + :A2 (Float, _lhs, _rhs), + name (_name) +{ +} + +void Relop::print (ostream & out) const +{ + out << "op" << name << type.suffix_name() << "(" << lhs << ", " << rhs << ")"; +} + +////////////////////////////////////////////////////////////////////// +/// AssOp + +AssOp::AssOp (Type ty, string _name, ENode _lhs, ENode _rhs) + :A2 (ty, _lhs, _rhs), + name (_name) +{ +} + +void AssOp::print (ostream & out) const +{ + out << lhs << " " << name << " " << rhs; +} + +////////////////////////////////////////////////////////////////////// +/// SelfModOp + +SelfModOp::SelfModOp (Type ty, string _name, ENode _child) + :A1 (ty, _child), + name (_name) +{ +} + +void SelfModOp::print (ostream & out) const +{ + out << child << " " << name; +} + +////////////////////////////////////////////////////////////////////// +/// + +////////////////////////////////////////////////////////////////////// + +} +} diff --git a/o3d/compiler/puritan/exp_gen.h b/o3d/compiler/puritan/exp_gen.h new file mode 100644 index 0000000..9387d98 --- /dev/null +++ b/o3d/compiler/puritan/exp_gen.h @@ -0,0 +1,331 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef PURITAN_EXPGEN_H +#define PURITAN_EXPGEN_H +#include "puritan.h" + +// Expression trees. +namespace Salem +{ +namespace Puritan +{ +class Exp; +class Gen; +class Type; +class Context; +typedef ::shared_ptr<Exp> ENode; +typedef std::vector <ENode> exp_list; +// Make a tree. +ENode create_expression (Gen *gen, Type return_type, const Context &ctx); + +// Print it out. +std::ostream & operator<< (std::ostream &out, const ENode &x); + +class Exp +{ +public: + Type type; + Exp (Type _type) ; + virtual ~Exp(); + + virtual void print (ostream &out) const = 0; + virtual bool is_fconstant() const; + + static ENode convert (ENode from, Rand *rand, Type to, Gen *gen); +}; + +class A0 : public Exp +{ +public: + A0 (Type ty); +}; + +class A1 : public Exp +{ +public: + ENode child; + A1 (Type ty, ENode child); +}; + +class A2 : public Exp +{ +public: + ENode lhs; + ENode rhs; + A2(Type ty, + ENode lhs, + ENode rhs); +}; + +class A3 : public Exp +{ +public: + ENode a0; + ENode a1; + ENode a2; + + A3(Type ty, + ENode a0, + ENode a1, + ENode a2); +}; + +class A4 : public Exp +{ +public: + ENode a0; + ENode a1; + ENode a2; + ENode a3; + + A4(Type ty, + ENode a0, + ENode a1, + ENode a2, + ENode a3); +}; + +class Unop : public A1 +{ + string name; + +public: + Unop(Type ty, string _name, ENode child) ; + void print (ostream &out) const; +}; + +class FCallTemplate : public Exp +{ +public: + FunctionSPtr target; + EList actuals; + + FCallTemplate (TypeList ty, FunctionSPtr f, EList actuals); + void print (ostream &out) const; +}; + +class SamplerRef : public A1 +{ + unsigned n; + +public: + SamplerRef (unsigned _n, ENode _child) ; + void print (ostream &out) const; +}; + +class SamplerSizeRef : public A0 +{ + unsigned n; + +public: + SamplerSizeRef (unsigned _n); + void print (ostream &out) const; +}; + +class UniformRef : public A0 +{ + string name; + +public: + UniformRef (Type ty, string v); + void print (ostream &out) const; +}; + +class Constant : public A0 +{ + string val; +public: + Constant (Type ty, string v); + Constant (Type ty, unsigned v); + bool is_fconstant() const; + void print (ostream &out) const; +}; + +class Variable : public A0 +{ + Decl decl; + +public: + Variable (Decl _d) ; + void print (ostream &out) const; +}; + + +class Index : public A2 +{ +public: + Index (ENode child, ENode index); + void print (ostream &out) const; + +}; + +class ConstArrayRef : public A0 +{ + Decl decl; +public: + ConstArrayRef (Decl _d); + void print (ostream &out) const; +}; + +class LHSVariable : public Exp +{ + Decl decl; + bool swizzled; + +public: + LHSVariable (Decl _d, bool _swizzled) ; + + void print (ostream &out) const; +}; + +class SpecialPhrase : public A1 +{ + string name; + +public: + SpecialPhrase (Type , string name, ENode _child); + + void print (ostream &out) const; +}; + +class UnIntrinsicFunc : public A1 +{ + string name; + +public: + UnIntrinsicFunc(Type ty, string _name, ENode) ; + + void print (ostream &out) const; +}; + +class BiIntrinsicFunc : public A2 +{ + string name; + +public: + BiIntrinsicFunc(Type ty, string _name, ENode _lhs, ENode _rhs) ; + + void print (ostream &out) const; +}; + +class TriIntrinsicFunc : public A3 +{ + string name; +public: + TriIntrinsicFunc(Type ty, string _name, ENode _a1, ENode _a2, ENode _a3); + + void print (ostream &out) const; +}; + +class Float4Func : public A4 +{ +public: + Float4Func(Type ty, ENode _a1, ENode _a2, ENode _a3, ENode _a4); + + void print (ostream &out) const; +}; + +class Convert : public A1 +{ + Type from; + string pre; + string post; + unsigned n; + +public: + Convert (Type _to, + Type _from, + string pre, + string post, + unsigned n, + ENode _child); + + void print (ostream &out) const; +}; + + +class Swizzle : public A1 +{ + string name; +public: + Swizzle (Type ty, string _name, ENode _child) ; + + void print (ostream &out) const; +}; + +class Binop : public A2 +{ + string name; + +public: + Binop(Type ty, string _name, ENode _lhs, ENode _rhs) ; + + void print (ostream &out) const; +}; + +class Relop : public A2 +{ + string name; + +public: + Relop(string _name, ENode _lhs, ENode _rhs) ; + void print (ostream &out) const; +}; + +class AssOp : public A2 +{ + string name; + +public: + AssOp(Type ty, + string _name, + ENode lhs, + ENode rhs); + + void print (ostream &out) const; +}; + +class SelfModOp : public A1 +{ + string name; + +public: + SelfModOp(Type ty, string _name, ENode lhs); + void print (ostream &out) const; +}; + +} +} +#endif + diff --git a/o3d/compiler/puritan/knobs.cc b/o3d/compiler/puritan/knobs.cc new file mode 100644 index 0000000..d36a89e --- /dev/null +++ b/o3d/compiler/puritan/knobs.cc @@ -0,0 +1,783 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "knobs.h" +#include <sstream> +#include <iomanip> +#include <iostream> +#include "rand.h" + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +// Knobs control the behavior of Puritan and keep track of what's happened. + +namespace Salem +{ +namespace Puritan +{ +static Knob *link; + +Knobs::Knobs (void) + : + top_level_statements +("--top-level-statements", + "\t\tNumber of statements at the top level\n", + 1, + 3), + + for_count + ("--for-count", + "\t\tNumber of for loops in code\n", + 0, + 2), + + while_count + ("--while-count", + "\t\tNumber of while loops in code\n", + 0, + 2), + + do_count + ("--do-count", + "\t\tNumber of do loops in code\n", + 0, + 1), + + if_count + ("--if-count", + "\t\tNumber of ifs in code\n", + 0, + 5), + + block_count + ("--block-count", + "\t\tNumber of blocks in code\n", + 0, + 3), + + code_limit + ("--code-limit", + "\t\tMaximum number of code nodes\n", + 200), + + exp_limit + ("--exp-limit", + "\t\tMaximum number of expression nodes\n", + 100), + + exp_count + ("--exp-count", + "\t\tNumber of expressions in code\n", + 6, + 16), + + func_count + ("--func-count", + "\t\tNumber of functions in code\n", + 1, + 9), + + func_trim + ("--func-trim", + "\t\tSize factor to apply to callee functions\n", + 0.7), + + arg_in_chance + ("--args-in-chance", + "\t\tChance argument may be just input\n", + 0.4), + + arg_out_chance + ("--args-out-chance", + "\t\tAfter input, chance argument may be just output\n", + 0.4), + + for_nesting + ("--for-nest", + "\t\tNumber of for loops inside for loops\n", + 0, + 9), + + while_nesting + ("--while-nest", + "\t\tNumber of while loops inside while loops\n", + 0, + 4), + + block_nesting + ("--block-nest", + "\t\tNumber of blocks inside blocks\n", + 0, + 1), + + do_nesting + ("--do-nest", + "\t\tNumber of do loops inside do loops\n", + 0, + 4), + + if_nesting + ("--if-nest", + "\t\tNumber of ifs inside ifs\n", + 0, + 9), + + block_length + ("--block-length", + "\t\tNumber of statements in a block.\n", + 2, + 7), + + expression_depth + ("--exp-depth", + "\t\tRange of expression complexity\n", + 1, + 7), + + special_chance + ("--special-chance", + "\t\tProbability at each expression point of a special phrase.\n", + 0.2), + + float4_chance + ("--float4-chance", + "\t\tProbability that an expression will have a float4 constructor.\n", + .03), + + float4_struct_member_chance + ("--float4-struct-member-chance", + "\t\tProbability that an element of the output struct will be float4.\n", + .90), + + float2_chance + ("--float2-chance", + "\t\tProbability that an expression will have a float2 constructor.\n", + 0.03), + + func_chance + ("--func-chance", + "\t\tAfter special, probability of an intrinsic function.\n", + 0.1), + + term_chance + ("--terminal-chance", + "\t\tAfter func, probability a terminal.\n", + 0.2), + + unary_chance + ("--unary-chance", + "\t\tAfter terminal, probabaility of a unary op.\n", + 0.05), + + swizzle_chance + ("--swizzle-chance", + "\t\tAfter unrary, chance that term will be a swizzle.\n", + 0.35), + + fcall_chance + ("--fcall-chance", + "\t\tAfter swizzle, probability of a function call.\n", + 0.25), + + copy_swizzle_chance + ("--copy-swizzle-chance", + "\t\tChance a simple non converting copy will be a swizzle.\n", + 0.3), + + noinline_chance + ("--fnoinline-chance", + "\t\tProbability a function will be declared noinline.\n", + 0.25), + + lhs_swizzle_chance + ("--lhs-swizzle-chance", + "\t\tProbabaility of a swizzle as an lval.\n", + 1.0), + + assop_chance + ("--assop-chance", + "\t\tProbability of a fancy assignment operator as assignment.\n", + 0.1), + + selfmod_chance + ("--selfmod-chance", + "\t\tProbability that an assignment will really be a selfmodify.\n", + 0.1), + + trifunc_chance + ("--trifunc-chance", + "\t\tProbability of a trinary intrinsic.\n", + 0.2), + + binfunc_chance + ("--binary-chance", + "\t\tAfter trinary, probabaility of a binary intrinsic instead of unary.\n", + 0.8), + + relop_chance + ("--relop-chance", + "\t\tIf there's going to be a binop," + "probability that it will be a relation.\n", + 0.1), + + relop_cond_chance + ("--relop-cond-chance", + "\t\tNear the end of an expression in a test, " + "chance that the op will be a compare\n", + 0.9), + + type_change_chance + ("--type-change-chance", + "\t\tProbability that the type of a subexpression will be different " + "to the expression.\n", + 0.01), + + type_float4_chance + ("--type-float4-chance", + "\t\tProbability that a subexpression type will be float4.\n", + 0.9), + + type_float2_chance + ("--type-float2-chance", + "\t\tAfter float4," + "probability that a subexpression type will be float2 instead of float.\n", + 0.9), + + sampler_count + ("--sampler-count", + "\t\tNumber of samplers used\n", + 0, + 9), + + sampler_chance + ("--sampler-chance", + "\t\tProbability that an expression will use a sampler\n", + .4), + + uniform_count + ("--uniform-count", + "\t\tNumber of unforms used\n", + 0, + 9), + + uniform_chance + ("--unform-chance", + "\t\tProbability that an expression will use a uniform\n", + .4), + + arg_count + ("--arg-count", + "\t\tNumber of arguments to functions\n", + 1, + 3), + + static_initializer_depth + ("--static-initializer-depth", + "\t\tRange of static initializer expression complexity\n", + 1, + 2), + + standalone + ("--standalone", + "\t\tIf the output should work outside the framework\n", + false), + + seed + ("--seed", + "\t\tSeed for the random number generator\n", + 0), + + variable_reuse + ("--variable-reuse", + "\t\tRatio of variables reused to created in expressions\n", + 0.90), + + array_use + ("--array-use", + "\t\tRatio of array terms in terms in expressions\n", + 0.2), + + array_reuse + ("--array-reuse", + "\t\tRatio of array terms reused to created in expressions\n", + 0.95), + + array_constness + ("--array-constness", + "\t\tProportion of array refs which are references to const arrays\n", + 1.), + + array_index_const + ("--array-index-const", + "\t\tProportion of array references which have a constant index\n", + 1.), + + array_in_for_use + ("--array-in-for", + "\t\tProportion of array references which use a loop index, lhs only.\n", + 0.), + + if_elses + ("--if-elses", + "\t\tProportion of ifs which have elses.\n", + 0.30), + + loop_breaks + ("--loop-breaks", + "\t\tProportion of loops which have breaks.\n", + 0.80), + + multiple_stmt + ("--multiple-stmt", + "\t\tRatio of single statements to blocks.\n", + 0.30), + + constant_use + ("--constant-use", + "\t\tRatio of constants to variables in expressions.\n", + 0.30), + + constant_small + ("--constant-small", + "\t\tChance a constant will be between 0 and 1.\n", + 0.90), + +// random_names +// ("--random-names", +// "\t\tShould names be random or easy on the eyes\n", +// false), + + int_variables + ("--int-variables", + "\t\tDeclare integer variables and use them in expressions\n", + false), + + allow_two_negs + ("--allow-two-negs", + "\t\tAllow two negs in an expression\n", + false), + first (link) +{ + link = 0; +} + +RangeKnob::RangeKnob (const char *_name, + const char *_help, + int _from, int _to) + :Knob (_name, _help), + from (_from), to (_to) +{ +} + +IntKnob::IntKnob (const char *_name, + const char *_help, + int _val) + :Knob (_name, _help), val (_val) +{ +} + +BoolKnob::BoolKnob (const char *_name, + const char *_help, + bool _x) + :Knob (_name, _help), val (_x) +{ +} + +ProbKnob::ProbKnob (const char *_name, + const char *_help, + double _x) + :Knob (_name, _help), prob (_x) +{ +} + +void RangeKnob::set (int a, int b) +{ + from = a; + to = b; +} + +// link together the members so we can iterate simply through the struct +// elements. + + +Knob::~Knob () +{ +} + +Knob::Knob (const char *_name, const char *_help) + :prev (link), + name (_name), help (_help) +{ + link = this; +} + + +void RangeKnob::to_stream (std::ostream & out) const +{ + out << name << "=" << from; + if (from != to) + out << "," << to; +} + +void IntKnob::to_stream (std::ostream & out) const +{ + out << name << "=" << val; +} + +void BoolKnob::to_stream (std::ostream & out) const +{ + out << name << "=" << (val ? "t" : "f") ; +} + +void BoolKnob::usage (std::ostream & out) const +{ + out << (name) << "=" << "[t|f] "; + out << (val ? "{t}" : "{f}") << "\n" << help; +} + +void ProbKnob::usage (std::ostream & out) const +{ + char t[1000]; + sprintf (t, "%s = <0.0 <= x <= 1.0> {%g}\n%s", name, prob, help); + out << t; +} + +void IntKnob::usage (std::ostream & out) const +{ + out << (name) << "=" << "<int>"; + out << " {" << val << "}\n" << help; +} + +void RangeKnob::usage (std::ostream & out) const +{ + out << (name) << "=" << "<int>,<int>"; + out << " {" << from; + if (from != to) + out << "," << to; + out << "}\n" << help; +} + +bool BoolKnob::set_from_argument (const char *arg) +{ + if (arg[0] == 't' || arg[0] == '1') + { + val = true; + return true; + } + if (arg[0] == 'f' || arg[0] == '0') + { + val = false; + return true; + } + return false; +} + +bool ProbKnob::set_from_argument (const char *arg) +{ + char *finish; + prob = strtod (arg, &finish); + return (arg != finish); +} + +bool IntKnob::set_from_argument (const char *arg) +{ + char *finish; + val = strtol (arg, &finish, 0); + return (arg != finish); +} + +int IntKnob::get () const +{ + return val; +} + +unsigned IntKnob::uget () const +{ + return val; +} + +bool RangeKnob::set_from_argument (const char *val) +{ + long int a, b; + char *next; + a = strtol (val, &next, 0); + + if (next == val) + { + return false; + } + + if (*next == 0) + { + from = a; + to = a; + return true; + } + + if (*next != ',') + { + return false; + } + + char *next_2; + next++; + b = strtol (next, &next_2, 0); + if (next_2 == next || *next_2 != 0) + return false; + + from = a; + to = b; + return true; +} + +bool Knobs::parse_argument (const char *arg) +{ + for (Knob * l = first; l; l = l->prev) + { + if (strncmp (l->name, arg, strlen (l->name)) == 0) + { + const char *val = strchr (arg, '='); + if (val) + return l->set_from_argument (val + 1); + } + } + return false; +} + +std::ostream & operator << (std::ostream &out, const Knobs& x) +{ + unsigned j = 0; + for (Knob * l = x.first; l; l = l->prev) + { + std::ostringstream tmp; + l->to_stream (tmp); + out << std::setw(30) << tmp.str(); + j++; + if (j == 3) + { + out << "\n"; + j = 0; + } + } + + return out; +} + +std::string Knobs::usage () +{ + std::ostringstream tmp; + Knobs dummy = Knobs (); + + for (Knob * l = dummy.first; l; l = l->prev) + { + l->usage (tmp); + tmp << "\n"; + } + + return tmp.str (); +} + +bool BoolKnob::operator () () const +{ + return val; +} + +void BoolKnob::set (bool x) +{ + val = x; +} + +bool ProbKnob::operator () (Rand * r) const +{ + double rnd = r->rnd_flt (); + return prob > rnd; +} + +void ProbKnob::to_stream (std::ostream & out) const +{ + char t[1000]; + sprintf (t, "%s=%g", name, prob); + out << t; +} + +double ProbKnob::get () const +{ + return prob; +} + +int RangeKnob::operator () (Rand * r) const +{ + return r->range (from, to); +} + +unsigned RangeKnob::random_uint (Rand * r) const +{ + return r->range (from, to); +} + +////////////////////////////////////////////////////////////////////// +// +// Coverage +// Used for random selections and maintaining stats. + +// pointer to head of choice list, so we can look through choices later and +// generate stats. +Coverage * Coverage::head = 0; + +Coverage::Coverage (std::string _title) + :title (_title), + prev (head) +{ + head = this; +} + +Coverage:: ~ Coverage () +{ +} + +void Coverage::increment (size_t idx) +{ + if (idx >= count.size ()) + { + count.resize (idx + 1); + + } + count[idx]++; +} + +std::ostream &operator<<(std::ostream &out, const Coverage &thing) +{ + thing.output_worker (out); + return out; +} + +StrCoverage:: StrCoverage (const char *_title, + const char **_selections):Coverage (_title) +{ + while (*_selections) + { + width = std::max (width, strlen (*_selections)); + selections.push_back (*_selections); + count.push_back (0); + _selections++; + } +// round to tab size + width = (width + 7) & -8; +} + +std::string StrCoverage::choose (class Rand * r) +{ + size_t idx = r->srange (0, count.size ()); + increment (idx); + return selections[idx]; +} + +void StrCoverage::output_worker (std::ostream & out) const +{ + unsigned items = 0; + out << title << "\n"; + size_t online = 0; + for (unsigned i = 0; i < count.size (); i++) + { + out << " |"; + out.fill (' '); + out.width (static_cast<int>(width)); + out << selections[i]; + out.fill (' '); + out.width (6); + out << count[i]; + out.width (0); + online += width + 6; + items++; + if (items > 4 || online > 60) + { + out << " |\n"; + items = 0; + online = 0; + } + } + if (online) + { + out << " |\n"; + } + if (prev) + { + prev->output_worker (out); + } +} + +IntCoverage::IntCoverage (const char *_title):Coverage (_title) +{ +} + +void IntCoverage::output_worker (std::ostream & out) const +{ + unsigned items = 0; + out << title << "\n"; + + for (unsigned i = 0; i < count.size (); i++) + { + out << "|"; + out << std::setw(4) << i ; + + if (count[i]) + { + out << std::setw(6) << count[i]; + } + else + { + out << std::setw(6) << "****"; + } + items++; + if (items > 4) + { + out << "\n"; + items = 0; + } + } + + if (items) + { + out << "\n"; + } + + if (prev) + { + prev->output_worker (out); + } +} + + +} +} diff --git a/o3d/compiler/puritan/knobs.h b/o3d/compiler/puritan/knobs.h new file mode 100644 index 0000000..00fa692 --- /dev/null +++ b/o3d/compiler/puritan/knobs.h @@ -0,0 +1,268 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef PURITAN_KNOBS_H +#define PURITAN_KNOBS_H + +#include <string> +#include <list> +#include <vector> + +namespace Salem +{ +namespace Puritan +{ + +class OutputInfo +{ +public: + typedef enum + { + Float1, + Float2, + Float4 + } ArgSize; + + unsigned n_samplers; + std::list <std::pair <ArgSize, std::string> > uniforms; + std::list <ArgSize> returns; +}; + +class Knob +{ + class Knob *prev; + const char *name; + const char *help; + + virtual bool set_from_argument (const char *val) = 0; + bool parse_argument (const char *val); + virtual void usage (std::ostream &out) const= 0; + + friend class Knobs; + friend class IntKnob; + friend class RangeKnob; + friend class BoolKnob; + friend class ProbKnob; + friend std::ostream & operator << (std::ostream &out, const class Knobs& x); + +public: + Knob(const char *name, const char *desc); + virtual ~Knob(); + + virtual void to_stream (std::ostream &) const = 0; + +}; + +class RangeKnob : public Knob +{ + int from; + int to; + + bool set_from_argument (const char *val); + void usage (std::ostream &) const; + +public: + RangeKnob (const char *, const char *,int f, int t); + + void set(int, int); + int operator () (class Rand *) const; + unsigned int random_uint (class Rand *) const ; + void to_stream (std::ostream &) const; + +}; + +class IntKnob : public Knob +{ + int val; + + bool set_from_argument (const char *val); + void usage (std::ostream &) const; + +public: + IntKnob (const char *, const char *,int); + + int get() const; + unsigned uget() const; + void set(int x) { val = x;} + void to_stream (std::ostream &) const; + +}; + +class BoolKnob : public Knob +{ + bool val; + + bool set_from_argument (const char *val); + void usage (std::ostream &) const; +public: + BoolKnob (const char *, const char *,bool f); + void to_stream (std::ostream &) const; + bool operator () () const; + void set(bool x); +}; + +class ProbKnob : public Knob +{ + double prob; + + bool set_from_argument (const char *val); + void usage (std::ostream &) const; +public: + ProbKnob (const char *, const char *,double d); + void set (double x) { prob = x; } + double get() const; + void to_stream (std::ostream &) const; + bool operator () (Rand *r) const ; +}; + +class Knobs +{ +public: + RangeKnob top_level_statements; + RangeKnob for_count; + RangeKnob while_count; + RangeKnob do_count; + RangeKnob if_count; + RangeKnob block_count; + IntKnob code_limit; + IntKnob exp_limit; + RangeKnob exp_count; + RangeKnob func_count; + ProbKnob func_trim; + ProbKnob arg_in_chance; + ProbKnob arg_out_chance; + RangeKnob for_nesting; + RangeKnob while_nesting; + RangeKnob block_nesting; + RangeKnob do_nesting; + RangeKnob if_nesting; + RangeKnob block_length; + RangeKnob expression_depth; + ProbKnob special_chance; + ProbKnob float4_chance; + ProbKnob float4_struct_member_chance; + ProbKnob float2_chance; + ProbKnob func_chance; + ProbKnob term_chance; + ProbKnob unary_chance; + ProbKnob swizzle_chance; + ProbKnob fcall_chance; + ProbKnob copy_swizzle_chance; + ProbKnob noinline_chance; + ProbKnob lhs_swizzle_chance; + ProbKnob assop_chance; + ProbKnob selfmod_chance; + ProbKnob trifunc_chance; + ProbKnob binfunc_chance; + ProbKnob relop_chance; + ProbKnob relop_cond_chance; + ProbKnob type_change_chance; + ProbKnob type_float4_chance; + ProbKnob type_float2_chance; + RangeKnob sampler_count; + ProbKnob sampler_chance; + RangeKnob uniform_count; + ProbKnob uniform_chance; + RangeKnob arg_count; + RangeKnob static_initializer_depth; + BoolKnob standalone; + IntKnob seed; + ProbKnob variable_reuse; + ProbKnob array_use; + ProbKnob array_reuse; + ProbKnob array_constness; + ProbKnob array_index_const; + ProbKnob array_in_for_use; + ProbKnob if_elses; + ProbKnob loop_breaks; + ProbKnob multiple_stmt; + ProbKnob constant_use; + ProbKnob constant_small; + BoolKnob int_variables; + BoolKnob allow_two_negs; + + Knob *first; + Knobs(void); + void to_stream (std::ostream &); + bool parse_argument (const char *arg); + static std::string usage (); +}; + +class Coverage +{ +private: + // Next in chain + + std::string title; + std::vector <int> count; + Coverage *prev; + + friend class StrCoverage; + friend class IntCoverage; + +public: + static Coverage *head; + virtual void output_worker (std::ostream & out) const = 0; + Coverage (std::string _title) ; + virtual ~ Coverage () ; + + void increment (size_t idx); +}; + +class StrCoverage:Coverage +{ +private: + std::vector <std::string> selections; + size_t width; + void output_worker (std::ostream & out) const; + +public: + StrCoverage (const char *_title, const char **_selections); + std::string choose (class Rand * r); +}; + +class IntCoverage:public Coverage +{ +private: + void output_worker (std::ostream & out) const; +public: + IntCoverage (const char *_title); +}; + + +std::ostream & operator << (std::ostream &out, const class Salem::Puritan::Coverage &x); +std::ostream & operator << (std::ostream &out, const class Salem::Puritan::Knobs &x); +} +} + +#endif diff --git a/o3d/compiler/puritan/main.cc b/o3d/compiler/puritan/main.cc new file mode 100644 index 0000000..cc6570a --- /dev/null +++ b/o3d/compiler/puritan/main.cc @@ -0,0 +1,129 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +// This program demonstrates how to get test case information out of Puritan. + +#include <iostream> +#include "test_gen.h" +#include "knobs.h" + +static std::string name_of_size(Salem::Puritan::OutputInfo::ArgSize x) { + switch (x) { + case Salem::Puritan::OutputInfo::Float1: + return "float"; + case Salem::Puritan::OutputInfo::Float2: + return "float2"; + case Salem::Puritan::OutputInfo::Float4: + return "float4"; + default: + break; + } + return "Impossible"; +} + + +static std::string comma(bool * need_comma) { + if (*need_comma) { + return ", "; + } else { + *need_comma = true; + return ""; + } +} + +int main (int argc, char * const argv[]) { + int j = 0; + if (argc > 1) { + sscanf(argv[1], "%d", &j); + } + + Salem::Puritan::Knobs options; + + // Set up some options just the way we like + options.block_count.set(2, 3); + options.for_count.set(2, 3); + options.for_nesting.set(2, 3); + options.array_in_for_use.set(false); + options.seed.set(j); + + Salem::Puritan::OutputInfo info; + + + // Build a test case + std::string test_case = + Salem::Puritan::generate(&info, options); + + // Dump out the test information + std::cout << "(Seed " << options.seed.get() << "), " + << "(Samplers ("; + + bool need_comma = false; + + for (unsigned i = 0; i < info.n_samplers; i++) { + std::cout << comma(&need_comma) << "in" << i; + } + + std::cout << "))," + << "(Uniforms ("; + + need_comma = false; + + for (std::list < + std::pair < + Salem::Puritan::OutputInfo::ArgSize, + std::string > >::const_iterator + i = info.uniforms.begin(); + i != info.uniforms.end(); + i++) { + std::cout << comma(&need_comma) + << name_of_size(i->first) + << " " + << i->second; + } + + std::cout << ")), " + << "(return struct {"; + + need_comma = false; + for (std::list <Salem::Puritan::OutputInfo::ArgSize>::const_iterator + i = info.returns.begin(); + i != info.returns.end(); + i++) { + std::cout << comma(&need_comma) << name_of_size(*i); + } + + std::cout << "})\n"; + std::cout << test_case; + + return 1; +} diff --git a/o3d/compiler/puritan/puritan.cc b/o3d/compiler/puritan/puritan.cc new file mode 100644 index 0000000..055b814 --- /dev/null +++ b/o3d/compiler/puritan/puritan.cc @@ -0,0 +1,1619 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#include <sstream> +#include <iterator> +#include <iomanip> +#include "puritan.h" +#include "knobs.h" +#include "structure_gen.h" +#include "exp_gen.h" +#include "puritan_assert.h" + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +namespace Salem +{ +namespace Puritan +{ +// Counters for code coverage. +IntCoverage cfor_count ("for loop count"); +IntCoverage cfor_nesting ("for loop nesting"); +IntCoverage cdo_count ("do loop count"); +IntCoverage cdo_nesting ("do loop nesting"); +IntCoverage cwhile_count ("while loop count"); +IntCoverage cwhile_nesting ("while loop nesting"); +IntCoverage cif_count ("if count"); +IntCoverage cif_nesting ("if nesting"); +IntCoverage cblock_count ("block count"); +IntCoverage cblock_nesting ("block nesting"); +IntCoverage cexp_count ("exp count"); +IntCoverage cfunc_count ("func count"); +IntCoverage csampler_count ("sampler count"); +IntCoverage cuniform_count ("unform count"); + +const char *assop_names[] = { "*=", "/=", "+=", "-=", 0 }; +StrCoverage assops ("Assops", assop_names); + +const char *selfmodops_names[] = { "++", "--", 0 }; +StrCoverage selfmodops ("SelfModOps", selfmodops_names); + +const char *lhs_f4tof4_swizzle_names[] = +{ + "w", "z", "zw", "y", + "yw", "yz", "yzw", "x", + "xw", "xz", "xzw", "xy", + "xyw", "xyz", "xyzw", + 0 +}; + +StrCoverage lhs_f4tof4_swizzles ("Lhs f4tof4 Swizzles", + lhs_f4tof4_swizzle_names); + +const char *lhs_f2tof2_swizzle_names[] = +{ + "xy", "x", "y", + 0 +}; + +StrCoverage lhs_f2tof2_swizzles ("lhs_f2tof2_swizzles", + lhs_f2tof2_swizzle_names); + + + + +// Build the control structurte for each function +// +// Each function starts off with an empty list of tokens describing what to +// do. Knobs and random numbers insert things into the list in random places +// to mark structure. Many things have two items in the list, for example, a +// for will have For token and a Close token, marking the point that the for +// scope is opened, and then closed. If something is inserted into the list +// between the For and the Close, then it becomes nested inside the for when +// the code is output. If the thing that is inserted is a pair and it has a +// Close istelf and that straddles the Close of the for, then since the Closes +// all look alike, it is still the same as being nested, except the scope of +// the for then stretches out to the end of the new item. + + +void Gen::create_control_structure () +{ + // Divide the samplers among the functions, samples_per_func is a list of + // samplers used in each function. + std::vector <UList> samples_per_func; + + for (unsigned fidx = 0; fidx < max_funcs; fidx++) + { + samples_per_func.push_back (UList ()); + } + + for (unsigned i = 0; i < n_samplers; i++) + { + for (unsigned j = 0; j < rand.urange (1,2); j++) + { + samples_per_func[rand.urange (0, max_funcs)].push_back (i); + } + } + + for (unsigned fidx = 0; fidx < max_funcs; fidx++) + { + unsigned for_count + = coverage (fidx, knobs.for_count, &cfor_count); + unsigned for_nesting + = coverage (fidx, knobs.for_nesting, &cfor_nesting); + unsigned block_count + = coverage (fidx, knobs.block_count, &cblock_count); + unsigned block_nesting + = coverage (fidx, knobs.block_nesting, &cblock_nesting); + unsigned while_count + = coverage (fidx, knobs.while_count, &cwhile_count); + unsigned while_nesting + = coverage (fidx, knobs.while_nesting, &cwhile_nesting); + unsigned do_count + = coverage (fidx, knobs.do_count, &cdo_count); + unsigned do_nesting + = coverage (fidx, knobs.do_nesting, &cdo_nesting); + unsigned if_count + = coverage (fidx, knobs.if_count, &cif_count); + unsigned if_nesting + = coverage (fidx, knobs.if_nesting, &cif_nesting); + unsigned asn_count + = coverage (fidx, knobs.exp_count, &cexp_count); + + std::vector <BlockSPtr> code_stack; + std::vector <ForSPtr> for_stack; + + TokenVec tokens; + + unsigned asn_tmp_count = asn_count; + + while (code_nodes < knobs.code_limit.uget() + && (for_count || block_count || while_count + || do_count || if_count || asn_tmp_count)) + { + code_nodes ++; + for_count = ins_stmts (&tokens, + for_count, for_nesting, + TokFor, 1, true); + + block_count= ins_stmts (&tokens, + block_count, block_nesting, + TokBlock, 1, false); + + while_count = ins_stmts (&tokens, + while_count, while_nesting, + TokWhile, 1, true); + + do_count = ins_stmts (&tokens, + do_count, do_nesting, + TokDo, 1, true); + + if_count = ins_stmts (&tokens, + if_count, if_nesting, + TokIf, 1, false); + + if (knobs.selfmod_chance (&rand)) + { + asn_tmp_count = ins_stmts (&tokens, + asn_tmp_count, 1, + TokSelfMod, 0, false); + } + else + { + asn_tmp_count = ins_stmts (&tokens, + asn_tmp_count, 1, + TokAssign, 0, false); + } + + } + + + // The final statement is always a return, we stick it in an assignment + // slot + + tokens.push_back (TokReturn); + + asn_count ++; + + // Spread the function calls and samplers over a number of assignments, + // so that there's always at least one of the right thing per function. + std::vector <UList> sampler_slots; + std::vector <FunctionList> caller_slots; + + for (unsigned i = 0; i < asn_count; i++) + { + sampler_slots.push_back (UList ()); + caller_slots.push_back (FunctionList ()); + } + + for (UList::const_iterator sampler = samples_per_func[fidx].begin (); + sampler != samples_per_func[fidx].end (); + sampler++) + { + for (unsigned j = 0; j < rand.urange (1,3); j++) + { + sampler_slots[rand.range (0, asn_count)].push_back (*sampler); + } + } + + for (FunctionList::const_iterator + i = callees[fidx].begin (); + i != callees[fidx].end (); + i++) + { + for (unsigned j = 0; j < rand.urange (1,2); j++) + { + caller_slots[rand.range (0, asn_count)].push_back (*i); + } + } + + + ForSPtr last_for; + + asn_count = 0; + + FunctionSPtr cur = functions[fidx]; + + code_stack.push_back (cur); + + + for (TokenVec::iterator t = tokens.begin (); + t != tokens.end (); + t++) + { + Context ctx (cur->idx, + last_for, + &sampler_slots[asn_count], + &caller_slots[asn_count]); + + switch (*t) + { + case TokBlock: + { + BlockSPtr c (new Block ()); + code_stack.back ()->add_child (c); + code_stack.push_back (c); + for_stack.push_back (last_for); + break; + } + case TokFor: + { + Decl counter + = new_uninitialized_variable (Scope (Static, cur->idx), + Int); + // mark this variable such that its not assigned anywhere + counter.noWrites = true; + ForSPtr + f (new For (counter, + rand.range (0,10), + rand.range (11,20))); + last_for = f; + code_stack.back ()->add_child (f); + code_stack.push_back (f); + for_stack.push_back (f); + break; + } + case TokIf: + { + BlockSPtr + c (new IfTemplate (create_expression (this, + Float, + ctx.relop ()), + knobs.if_elses (&rand))); + code_stack.back ()->add_child (c); + code_stack.push_back (c); + for_stack.push_back (last_for); + break; + } + case TokWhile: + { + Decl counter + = new_uninitialized_variable (Scope (Static, cur->idx), + Float); + // mark this variable such that its not assigned anywhere + counter.noWrites = true; + BlockSPtr + c (new While (create_expression (this, + Float, + ctx.relop ()), + counter, + rand.range (1,10))); + code_stack.back ()->add_child (c); + code_stack.push_back (c); + for_stack.push_back (last_for); + break; + } + case TokDo: + { + Decl counter + = new_uninitialized_variable (Scope (Static, cur->idx), + Float); + // mark this variable such that its not assigned anywhere + counter.noWrites = true; + BlockSPtr + c (new Do (create_expression (this, + Float, + ctx.relop ()), + counter, + rand.range (1,10))); + code_stack.back ()->add_child (c); + code_stack.push_back (c); + for_stack.push_back (last_for); + break; + } + // End of any kind of block + case TokClose: + code_stack.pop_back (); + last_for = for_stack.back (); + for_stack.pop_back (); + break; + case TokSelfMod: + { + Type ty = random_type (); + Decl d = fetch_decl (ty, ctx, Initialized, ReadWrite); + ENode lhs = ENode + (new SelfModOp (ty, selfmodops.choose (&rand), + ENode (new LHSVariable (d, false)))); + // We can use the assignment template for self mod, since it + // takes any expression. + code_stack.back ()->add_child + (CodeSPtr (new AssignmentTemplate (lhs))); + } + break; + case TokAssign: + { + ENode rhs; + ENode lhs; + Type type (NoType); + bool assop = knobs.assop_chance (&rand); + bool lhs_swizzle = true; + // Inside a for loop we may want to do array assignments. + if (ctx.loop.get() + && knobs.array_in_for_use (&rand) + && !knobs.array_constness (&rand)) + { + Decl decl = gen_array_decl (Float4Array, ctx, Read); + rhs = create_expression (this, Float4, ctx); + lhs = ENode + (new Index (ENode (new LHSVariable (decl, false)), + ENode (new Constant (Float4, "4")))); + type = Float4; + } + else + { + type = random_type (); + rhs = create_expression (this, type, ctx); + // An assignment operator needs a preinitialized lhs. + Decl d = fetch_decl (type, ctx, + assop + ? Initialized + : Uninitialized, + assop + ? ReadWrite + : Write); + if (! d.initializer.get() ) + lhs_swizzle = false; + lhs = ENode (new LHSVariable (d, false)); + } + + + if (assop) + { + rhs = ENode (new AssOp (type, assops.choose (&rand), + lhs, + rhs)); + } + else + { + if (lhs_swizzle) + { + if (type == Float4 && knobs.lhs_swizzle_chance (&rand)) + { + lhs = ENode + (new Swizzle (type, + lhs_f4tof4_swizzles.choose (&rand), + lhs)); + } + else if (type == Float2 && knobs.lhs_swizzle_chance (&rand)) + { + lhs = ENode + (new Swizzle (type, + lhs_f2tof2_swizzles.choose (&rand), + lhs)); + } + } + rhs = ENode (new AssOp (type, "=", lhs, rhs)); + } + + code_stack.back ()->add_child + (CodeSPtr (new AssignmentTemplate (rhs))); + asn_count ++; + } + break; + + case TokReturn: + { + // Add the returns at the bottom, the main function returns a + // struct. + + if (cur->idx == 0) + { + Decl decl = new_variable (Scope (Static, cur->idx), + cur->ret_type); + EList v; + for (TypeList::const_iterator i = cur->ret_type.begin (); + i != cur->ret_type.end (); + i++) + { + v.push_back (create_expression (this, *i, ctx)); + } + code_stack.back ()->add_child + (CodeSPtr (new Return (v, decl))); + } + else + { + ENode rval1 = create_expression (this, + cur->ret_type.front (), + ctx); + code_stack.back ()->add_child + (CodeSPtr (new Return (rval1))); + } + } + break; + + case TokBreak: + { + BreakSPtr + b (new Break + (create_expression (this, Float, ctx))); + code_stack.back ()->add_child (b); + } + break; + default: + PURITAN_ABORT("Illegal token " << static_cast<int>(*t)); + break; + } + } + } +} + + +// Create the function template for each function and fill in the argument type +// and return types. + +void Gen::create_call_structure () +{ + for (unsigned i = 0; i < max_funcs; i++) + { + DeclList formals; + TypeList ret_type; + + if (i == 0) + { + // first function is main, always returns a struct and takes just + // float2 + + unsigned nrets = rand.urange (1, 5); + for (unsigned j = 0; j < nrets; j++) + { + ret_type.push_back (knobs.float4_struct_member_chance (&rand) + ? Float4 + : Float); + + } + + formals.push_back ( + new_uninitialized_variable (Scope (Argument_I, i), Float2)); + } + else + { + switch (rand.range (0,4)) + { + case 0: + ret_type.push_back (Float); + break; + case 1: + ret_type.push_back (Float2); + break; + default: + ret_type.push_back (Float4); + break; + } + + unsigned n = knobs.arg_count.random_uint (&rand); + + for (unsigned j = 0; j < n; j++) + { + Context ctx (i, ForSPtr (), 0, 0); + scope_t arg = + knobs.arg_in_chance(&rand) + ? Argument_I : knobs.arg_out_chance (&rand) + ? Argument_O : Argument_IO; + + formals.push_back (new_uninitialized_variable (Scope (arg, i), + random_type ())); + } + } + functions.push_back (FunctionSPtr + (new Function (this, i, ret_type, + formals, + knobs.standalone (), + knobs.noinline_chance(&rand)))); + } + // Different call patterns, so far only one. + switch (int x = rand.range (0,0)) + { + case 0: + // Each functions calls the one above, save the last + for (unsigned i = 0; i < max_funcs; i++) + { + FunctionList targets; + if (i != (max_funcs - 1)) + { + targets.push_back (functions[i+1]); + } + callees.push_back (targets); + } + break; + case 1: + break; + default: + PURITAN_ABORT ("Unexpected case " << x); + } +} + +// Add any samplers to the symbol table. +void Gen::declare_samplers () +{ + for (unsigned i = 0; i < n_samplers; i++) + { + new_uninitialized_variable (Scope (Sampler), SamplerFloat4, i); + new_uninitialized_variable (Scope (Uniform), SamplerSize, i); + } + + for (unsigned i = 0; i < n_uniforms; i++) + { + Decl decl = new_uninitialized_variable (Scope (Uniform), Float4, i); + ostringstream decl_name; + decl_name << decl; + uniforms.push_back + (std::pair <Type, string> (Float4, decl_name.str())); + } +} + +// Insert the statement tokens somewhere randomly. +unsigned Gen::ins_stmts (TokenVec *vec, + unsigned count, + unsigned nest, + Token token, + unsigned ends, + bool break_p) +{ + nest = std::min (std::max(1U, nest), count); + size_t start_pos = 0; + size_t limit = vec->size(); + size_t end_pos = limit; + for (unsigned j = 0; j < nest; j++) + { + start_pos = rand.srange (start_pos, end_pos); + end_pos = start_pos + 1 < end_pos + ? rand.srange (start_pos + 1, end_pos) + : end_pos; + + PURITAN_ASSERT (start_pos <= end_pos, "Sanity check"); + + vec->insert (vec->begin () + start_pos, token); + + start_pos ++; + end_pos++; + + if (break_p && knobs.loop_breaks (&rand)) + { + vec->insert (vec->begin () + start_pos, TokBreak); + start_pos ++; + end_pos++; + } + + if (ends) + { + vec->insert (vec->begin () + start_pos, TokClose); + start_pos ++; + end_pos++; + } + + } + return count - nest; +} + +////////////////////////////////////////////////////////////////////// +// Class containing the scope of a name. +Scope::Scope (scope_t x, unsigned i) : scope (x) , fnc_idx (i) +{ + +} + +bool Scope::visible_in (const Context &ctx, + Dir d) const +{ + switch (scope) + { + case Argument_IO: + return fnc_idx == ctx.func && (d == Read || d == Write); + case Argument_O: + return fnc_idx == ctx.func && (d == Write); + case Argument_I: + return fnc_idx == ctx.func && (d == Read || d == Write); + case StaticConstArrays: + case Sampler: + case Uniform: + return d == Read; + case Static: + return fnc_idx == ctx.func; + case NoScope: + default: + break; + } + PURITAN_ABORT ("Illegal scope " << scope); +} + +string Scope::name () const +{ + switch (scope) + { + case Sampler: + return "sampler "; + case Uniform: + return "uniform "; + case Static: + case StaticConstArrays: + return ""; + default: + case NoScope: + case Argument_I: + case Argument_O: + case Argument_IO: + PURITAN_ABORT ("Argument error " << scope); + } +} + +string Scope::short_name () const +{ + switch (scope) + { + case Sampler: + return "in"; + case Uniform: + return "u_"; + case Static: + case StaticConstArrays: + return "s_"; + case Argument_I: + return "ai_"; + case Argument_O: + return "ao_"; + case Argument_IO: + return "aio_"; + default: + case NoScope: + PURITAN_ABORT ("Argument error " << scope); + } +} + +bool Scope::eq (const Scope &other) const +{ + return scope == other.scope && fnc_idx == other.fnc_idx; +} + +////////////////////////////////////////////////////////////////////// +// Generator +// Main wrapper and support +// Emit all the declarations at the given scope +void +Gen::output_declarations (ostream &out, + Scope s) +{ + DeclVec::const_iterator i = symtable.begin (); + while (i != symtable.end ()) + { + const Decl & d = *i; + if (d.scope.eq (s)) + { + if (d.type () == Float4Array) + { + out << "uniform float4 " + << d + << "[" << array_size << "];\n"; + } + else if (d.type () == Float4ConstArray) + { + out << "const float4 " + << d + << "[" << array_size << "] = {\n"; + for (unsigned i = 0; i < array_size * 4; i++) + { + if (i) + { + out << ",\n"; + } + + out << gen_fconstant (); + } + out << "};\n"; + } + else + { + out << d.scope.name (); + + if (d.scope.scope != Sampler) + { + out << " " << d.type; + } + + out << " " << d; + + if (d.initializer.get() ) + { + out << " = " << d.initializer; + } + out << ";\n"; + } + } + i++; + } +} + +// Return a random type. +Type Gen::random_type () +{ + return (knobs.type_float4_chance (&rand)) + ? Float4 + : knobs.type_float2_chance (&rand) + ? Float2 + : Float; +} + + +const char *id_char = +"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char *first_char = +"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char *digit = "012345678"; + +char Gen::rchar (const char *what) +{ + return what[ (rand.srange (0, strlen (what)))]; +} + + +// Make a random floating point constant. +string Gen::gen_fconstant () +{ + if (knobs.constant_small (&rand)) + { + ostringstream out; + char b[100]; + sprintf (b, "%g", 10.0 / rand.range (1, 10000)); + out << b; + return out.str (); + } + else + { + string res; + unsigned len = rand.range (1, 30); + unsigned point = rand.range (0,len); + + for (unsigned i = 0; i < len; i++) + { + res += rchar (digit); + if (i == point) + { + res += "."; + } + } + return res; + } +} + +ostream & Gen::output_typedefs (ostream &out) +{ + FunctionSPtr main = functions.front (); + + if (knobs.standalone()) + { + out << "\n\nstruct PS_OUTPUT\n{\n"; + unsigned k = 0; + for (TypeList::const_iterator i = main->ret_type.begin (); + i != main->ret_type.end (); + i++) + { + out << *i << " color" << k << " :" << "COLOR" << k << ";\n"; + k++; + } + out << "};\n"; + } +#if 0 + else + { + out << "#ifdef STANDALONE_DEFS\n" + << "#define D_IN(t,name) in t name\n" + << "#define D_OUT(t,name) out t name\n" + << "#define D_INOUT(t,name) inout t name\n" + << "#define D_VPOS : VPOS\n" + << "#define D_COLOR0 : COLOR0\n" + << "#define D_COLOR1 : COLOR1\n" + << "#define D_COLOR2 : COLOR2\n" + << "#define D_COLOR3 : COLOR3\n" + "\n\nstruct PS_OUTPUT\n{\n"; + + unsigned k = 0; + + for (TypeList::const_iterator i = main->ret_type.begin (); + i != main->ret_type.end (); + i++) + { + out << *i << " color" << k << " " << "D_COLOR" << k << ";\n"; + k++; + } + out << "};\n" + << "#endif\n" + << "#ifdef WRAPPED_DEFS\n" + << "#define D_IN(t,name) t name\n" + << "#define D_OUT(t,name) t&name\n" + << "#define D_INOUT(t,name) t&name\n" + << "etc etc\n" + << "#include \"something here\"\n" + << "#endif\n"; + } +#endif + + return out; +} + +// Create a new and unique name of the right type in the symbol table +Decl Gen::new_variable (Scope scope, + Type type, + Kind k, + const Context &ctx) +{ + return k == Initialized + ? new_initialized_variable (scope, type, ctx) + : new_uninitialized_variable (scope, type); +} + +Decl Gen::new_variable (Scope scope, + const TypeList & type) +{ + Decl newdecl = Decl (scope, Struct); + // We ignore the type, there's only one struct. + (void) type; + symtable.push_back (newdecl); + return newdecl; +} + +Decl Gen::new_uninitialized_variable (Scope scope, + Type type, + unsigned idx) +{ + Decl newdecl = Decl (scope, type, idx); + symtable.push_back (newdecl); + return newdecl; +} + +Decl Gen::new_uninitialized_variable (Scope scope, + Type type) +{ + Decl newdecl = Decl (scope, type); + symtable.push_back (newdecl); + return newdecl; +} + +Decl Gen::new_initialized_variable (Scope scope, + Type type, + const Context &ctx) +{ + Decl newdecl = Decl (scope, type, create_expression (this, type, ctx)); + symtable.push_back (newdecl); + return newdecl; +} + +////////////////////////////////////////////////////////////////////// +// Contexts +// A context is a structure which describes the conditions under something else +// is being run. An expression can use the context to work out if it is in a +// for loop or which function it's in. + +Context::Context (unsigned _owner, + ForSPtr _loop, + UList *_samplers, + FunctionList *_callees) + + : func (_owner), loop (_loop) , depth (0), relop_p (false), + samplers (_samplers), + callees (_callees) +{ +} + +Context Context::deeper () const +{ + Context res = *this; + res.depth++; + return res; +} + +Context Context::relop () const +{ + Context res =deeper (); + res.relop_p = true; + return res; +} + +Decl Gen::gen_array_decl (Type t, + const Context &ctx, + Dir d) +{ + if (knobs.array_reuse (&rand)) + { + DeclVec::iterator i = existing_decl (t, ctx, d); + if (i != symtable.end ()) + { + return *i; + } + } + return new_uninitialized_variable (Scope (Uniform), t); +} + +Decl Gen::fetch_decl (Type t, const Context & ctx, Kind k, Dir dir) +{ + if (knobs.variable_reuse (&rand)) + { + DeclVec::iterator i = existing_decl (t, ctx, dir); + if (i != symtable.end ()) + { + return *i; + } + } + return new_variable (Scope (Static, ctx.func), t, k, ctx); +} + +////////////////////////////////////////////////////////////////////// +// Simply reformat the output to make it look pretty. + +class Reformat +{ + string res; + string pending_line; + std::vector <unsigned> indent_stack; + unsigned last_indent; + bool pending_nl; + char prev_char; + char this_char; + bool had_open_p; + bool had_eq; + unsigned raw_line_length; + string tab () + { + string t; + unsigned i; + for (i = 0; i < last_indent; i++) + { + t += " "; + } + return t; + } + void write (string x) + { + pending_line += x; + } + unsigned pos_on_line () + { + return last_indent + pending_line.size (); + } + void flush_pending_line () + { + if (!pending_line.empty ()) + { + res += tab (); + res += pending_line; + res += "\n"; + pending_line = ""; + } + pending_nl = false; + had_open_p = false; + raw_line_length = 0; + last_indent = indent_stack.back (); + } + void tabout_and_writeln (string x) + { + tab (); + write (x); + write ("\n"); + } +public: + Reformat (string src) : res (""), + pending_line (""), + last_indent (0), + pending_nl (false), + prev_char (' '), + this_char (' '), + had_open_p (false), + had_eq (false), + raw_line_length (0) + + { + indent_stack.push_back (0); + prev_char = '\n'; + for (string::iterator p = src.begin (); p != src.end (); p++) + { + this_char = *p; + + if (raw_line_length == 0) + { + raw_line_length = last_indent; + for (string::iterator q = p; + q != src.end () && *q != '\n'; + q++) + { + raw_line_length++; + } + } + + switch (this_char) + { + case '{': + flush_pending_line (); + pending_line = "{"; + flush_pending_line (); + prev_char = this_char; + last_indent = indent_stack.back () + 2; + indent_stack.push_back (last_indent); + break; + // fall + case '(': + if (raw_line_length > 80 && pos_on_line () > 70) + { + flush_pending_line (); + } + + if (pos_on_line () < 30) + { + indent_stack.push_back (pos_on_line ()); + } + else + { + indent_stack.push_back (indent_stack.back ()+1); + } + pending_line += this_char; + prev_char = this_char; + + break; + + case '}': + + flush_pending_line (); + indent_stack.pop_back (); + pending_line = "}"; + last_indent = indent_stack.back (); + flush_pending_line (); + + + prev_char = '\n'; + break; + + case ')': + prev_char = this_char; + indent_stack.pop_back (); + pending_line += this_char; + break; + case '\n': + pending_nl = false; + flush_pending_line (); + prev_char = this_char; + break; + case ' ': + if (prev_char == ' ' + || prev_char == '(' + || prev_char == '\n' + || prev_char == '{') + { + break; + } + pending_line += this_char; + prev_char = this_char; + break; + + case ';': + pending_line += this_char; + prev_char = this_char; + if (had_eq) + { + had_eq = false; + indent_stack.pop_back (); + } + break; + + case '=': + pending_line += this_char; + prev_char = this_char; + if (!had_eq) + { + had_eq = true; + indent_stack.push_back (indent_stack.back () + 4); + } + + break; + + default: + // no need to break line if we know it will fit + + if (raw_line_length > 80) { + if (prev_char == ' ' + && (!isalpha (this_char)) + && (!isdigit (this_char)) + && pos_on_line () > 60) + { + flush_pending_line (); + } + else + if ( (prev_char == ' ' + && pos_on_line () > 100) + || pending_nl) + { + flush_pending_line (); + } + } + if (prev_char == ';') + { + pending_line += ' '; + } + + pending_line += this_char; + prev_char = this_char; + break; + } + } + flush_pending_line (); + } + string str () + { + return res; + } +}; + +ostream & +Gen::output_boiler_plate (ostream &out) const +{ + // isnan functions + out << + "float isnan1 (float a)\n" + "{\n" + "return ((a<0) && (a>0));" + "}\n" + "float2 isnan2 (float2 a)\n" + "{\n" + "return ((a<0) && (a>0));" + "}\n" + "float4 isnan4 (float4 a)\n" + "{\n" + "return ((a<0) && (a>0));" + "}\n"; + + out << + "float4 quick_mod (float4 a, float4 b)\n" + "{\n" + "float4 d = a / b;\n" + "float4 q = d - frac (d);\n" + "float4 r = a - q * b;\n" + "r -= frac(r);\n" + "return isnan4 (r) ? 0 : r;\n" + "}\n" + "float opcond(float x, float y, float z)\n" + "{\n" + "return x ? y : z;\n" + "}\n" + "float2 opcond2(float2 x, float2 y, float2 z)\n" + "{\n" + "return x ? y : z;\n" + "}\n" + "float4 opcond4(float4 x, float4 y, float4 z)\n" + "{\n" + "return x ? y : z;\n" + "}\n"; + + static char * floats[2][4] = + { { "float", "float2", "float4", 0}, + {"1", "2", "4", 0} }; + static char * ops[2][9] = + { {"lt", "le", "gt", "ge", "eq", "ne", "and", "or", 0}, + {"<", "<=", ">", ">=", "==", "!=", "&&", "||", 0} }; + + for (unsigned i = 0; floats[0][i]; i++) + { + for (unsigned t = 0; ops[0][t]; t++) + { + out << floats[0][i] << " op" << ops[0][t] << floats[1][i] + << "(" << floats[0][i] << " a, " << floats[0][i] << " b) " + << "{ return isnan" << floats[1][i] << " (a) ? 0 : (isnan" + << floats[1][i] << " (b) ? 0 : (a" << ops[1][t] << "b));\n}"; + } + + } + return out; +} + +std::ostream & operator << (std::ostream &out, const FunctionSPtr&x) +{ + x->output_code (out); + return out; +} + +std::ostream & operator << (std::ostream &out, + const Program &functions) +{ + std::copy (functions.rbegin (), + functions.rend (), + std::ostream_iterator<FunctionSPtr> (out,"\n")); + return out; +} + +std::ostream & operator << (std::ostream &out, gspair x) +{ + x.first->output_declarations (out, x.second); + return out; +} + +static OutputInfo::ArgSize translate_type (Type t) +{ + switch (t.type) + { + case Float4: + return OutputInfo::Float4; + case Float2: + return OutputInfo::Float2; + case Float: + return OutputInfo::Float1; + case NoType: + case Int: + case Int4: + case SamplerFloat4: + case SamplerSize: + case Struct: + case Float4ConstArray: + case Float4Array: + default: + PURITAN_ABORT ("Unexpected type " << t.type); + break; + } +} + +string Gen::generate (OutputInfo *output_info) +{ + ostringstream output_stream; + ostringstream comments; + + // Reset the declaration counter so names start at 0 for each new file. + Decl::reset (); + + declare_samplers (); + create_call_structure (); + create_control_structure (); + + output_typedefs (output_stream); + + output_stream << gspair (this, Scope (Sampler)) + << gspair (this, Scope (Uniform)) + << gspair (this, Scope (StaticConstArrays)); + + output_boiler_plate (output_stream); + + output_stream << functions + << "/* cn=" << code_nodes << " en=" << exp_nodes << "*/\n"; + + comments << "/*\n" << knobs << "*/\n" + << "/*\n Coverage to this point\n" + << *Coverage::head + << "*/\n"; + + Reformat reformatted (output_stream.str ()); + + + // Fill in the output description with what we've been up to. + if (output_info) + { + output_info->n_samplers = n_samplers; + + PURITAN_ASSERT (output_info->uniforms.empty(),"Incorrect start state"); + PURITAN_ASSERT (output_info->returns.empty(), "Incorrect start state"); + + FunctionSPtr func = functions[0]; + + for (TypeList::const_iterator i = func->ret_type.begin(); + i != func->ret_type.end(); + i++) + { + output_info->returns.push_front (translate_type (*i)); + } + + for (std::vector<std::pair <Type, string> >::const_iterator + i = uniforms.begin(); + i != uniforms.end(); + i++) + { + std::pair <OutputInfo::ArgSize, std::string> + x(translate_type (i->first), i->second); + output_info->uniforms.push_front (x); + } + } + return comments.str () + reformatted.str (); +} + +Gen::Gen (Knobs & _config) + : + rand (_config.seed.get ()), + knobs (_config), + exp_nodes (0), + code_nodes (0), + max_funcs (coverage (knobs.func_count, &cfunc_count)), + n_samplers (coverage (knobs.sampler_count, &csampler_count)), + n_uniforms (coverage (knobs.uniform_count, &cuniform_count)) +{ + +} + + +// Return iterator pointing to an existing decl with the suggested type and +// visible in the context. If it's an argument formal make sure that the +// direction of intended action is ok. +DeclVec::iterator Gen::existing_decl (Type t, + const Context &ctx, + Dir d) +{ + size_t length = symtable.size (); + size_t scan_from = rand.srange (0, length); + + // Scan from the middle somewhere, and if that doesn't find + // anything, start again at the start. + + DeclVec::iterator starts[2] = + { symtable.begin () + scan_from, symtable.begin()}; + DeclVec::iterator ends[2] = + { symtable.end(), symtable.begin() + scan_from }; + + for (unsigned i = 0; i < 2; i++) + { + for (DeclVec::iterator res = starts[i]; + res != ends[i]; + res++) + { + // It's usable if we're writing, or it's initialized, or an + // incoming argument. Also, check if assignments it are allowed, if + // we are writing. + if (res->type == t + && res->scope.visible_in (ctx, d) + && (res->initializer.get() + || d == Write + || res->scope.scope == Argument_I + || res->scope.scope == Argument_IO) + && (!res->noWrites || d == Read)) + { + return res; + } + } + } + return symtable.end (); +} + + +unsigned Decl::counter = 0; + +Decl::Decl () : scope (NoScope), type (NoType), idx (0), noWrites (false) +{ +} + +Decl::Decl (bool) : scope (NoScope), type (NoType), idx (0), noWrites (false) +{ +} + +Decl::Decl (Scope _scope, + Type _type) + : scope (_scope), type (_type), idx (++counter), noWrites (false) +{ +} + +Decl::Decl (Scope _scope, + Type _type, + unsigned _idx) + : scope (_scope), type (_type), idx (_idx), noWrites (false) +{ +} + + +// Return a number according to the knob range, and increment the coverage +// counter for it. +int Gen::coverage (const RangeKnob &knob, + IntCoverage *marker) +{ + int r = knob (&rand); + marker->increment (r); + return r; +} + +// As above, but the value is scaled so that minor functions get smaller +// values. +int Gen::coverage (unsigned fidx, + const RangeKnob &knob, + IntCoverage *marker) +{ + int r = knob (&rand); + if (fidx > 0) + { + r = (static_cast<int> ( (static_cast<double> (r)) + * knobs.func_trim.get ())); + } + marker->increment (r); + return r; +} + +////////////////////////////////////////////////////////////////////// +// Decl class +// Knows the scope, type, name and initializer of every variable. + +Decl::Decl (Scope _scope, + Type _type, + ENode _init) + : scope (_scope), + type (_type), + initializer (_init), + idx (++counter) +{ +} + +void Decl::reset () +{ + Decl::counter = 0; +} + +Scope::Scope (scope_t _scope) : scope (_scope), fnc_idx (0) +{ +} + +std::ostream & operator << (std::ostream &out, const Decl&x) +{ + if (x.type == SamplerSize) + { + out << "in" << x.idx << "_size"; + } + else + { + out << x.scope.short_name () << x.type.short_name () << x.idx; + } + return out; +} + +////////////////////////////////////////////////////////////////////// +// Type + +Type::Type () : type (NoType) {} +Type::Type (enum type _type) : type (_type) {} + +enum type Type::operator () () const +{ + return type; +} + +bool Type::array_p () +{ + return type == Float4ConstArray + || type == Float4Array; +} + + +std::ostream & operator << (std::ostream &out, Type x) +{ + const char *w; + switch (x.type) + { + case Int: + w= "int"; + break; + case Int4: + w="int4"; + break; + case Float: + w= "float"; + break; + case Float2: + w= "float2"; + break; + case Float4: + case SamplerSize: + w= "float4"; + break; + case Float4ConstArray: + w ="floatc4x40"; + break; + case Float4Array: + w = "floatv4x40"; + break; + case Struct: + w = "struct PS_OUTPUT"; + break; + default: + case NoType: + case SamplerFloat4: + PURITAN_ABORT ("Argument error" << x.type); + } + return (out << w); + +} + +string Type::short_name () const +{ + switch (type) + { + case Int: + return "i_"; + case Int4: + return "i4_"; + case Float: + return "f1_"; + case Float2: + return "f2_"; + case Float4: + return "f4_"; + case Float4ConstArray: + return "carray_"; + case Float4Array: + return "farray_"; + case SamplerFloat4: + return ""; + case Struct: + return "s_"; + default: + case NoType: + case SamplerSize: + PURITAN_ABORT ("Argument error:" << type); + } +} + +string Type::suffix_name () const +{ + switch (type) + { + case Int: + return "i"; + case Int4: + return "i4"; + case Float: + return "1"; + case Float2: + return "2"; + case Float4: + return "4"; + case Float4ConstArray: + return "c"; + case Float4Array: + return "f"; + case SamplerFloat4: + return ""; + case Struct: + return "s"; + default: + case NoType: + case SamplerSize: + PURITAN_ABORT ("Argument error:" << type); + } +} + +bool Type::operator != (Type x) const +{ + return x.type != type; +} + +bool Type::operator == (Type x) const +{ + return x.type == type; +} + +std::ostream & operator << (std::ostream &out, const TypeList &x) +{ + PURITAN_ASSERT (x.size () == 1, "Should never be called with a non unary list"); + out << x.front (); + return out; +} + + +} +} + diff --git a/o3d/compiler/puritan/puritan.h b/o3d/compiler/puritan/puritan.h new file mode 100644 index 0000000..a9dc409 --- /dev/null +++ b/o3d/compiler/puritan/puritan.h @@ -0,0 +1,259 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef PURITAN_PURITAN_H +#define PURITAN_PURITAN_H + +#include <string> +#include <sstream> +#include <vector> +#include <list> +#include "puritan_assert.h" +#include "rand.h" + +#include "puritan_shared_ptr.h" + +namespace Salem +{ +namespace Puritan +{ + +class Decl; +class Exp; +class For; +class Function; +class Gen; +class IntCoverage; +class RangeKnob; +class Scope; +class Type; + +typedef ::shared_ptr<Exp> ENode; +typedef ::shared_ptr<For> ForSPtr; +typedef ::shared_ptr<Function> FunctionSPtr; +typedef std::list <Decl> DeclList; +typedef std::list <ENode> EList; +typedef std::list <FunctionSPtr> FunctionList; +typedef std::list <Type> TypeList; +typedef std::list <unsigned> UList; +typedef std::ostream ostream; +typedef std::ostringstream ostringstream; +typedef std::pair <Gen *, Scope> gspair; +typedef std::string string; +typedef std::vector <Decl> DeclVec; +typedef std::vector <FunctionSPtr> Program; + +ostream & operator << (ostream &out, Type x); +ostream & operator << (ostream &out, const Decl& x); +ostream & operator << (ostream &out, const TypeList & x) ; +ostream & operator << (ostream &out, gspair x); + +const unsigned int array_size = 3; + +enum type +{ + NoType, + Int, Int4, + Float, Float2, Float4, + SamplerFloat4, + SamplerSize, + Struct, + Float4ConstArray, Float4Array +}; + +class Type +{ +public: + enum type type; + string name () const; + string short_name () const; + string suffix_name () const; + bool array_p (); + enum type operator () () const; + Type (enum type); + Type (); // default ctor for use with STL containers + bool operator == (Type) const; + bool operator != (Type) const; + unsigned struct_elements (); +}; + +typedef enum +{ + NoScope, + StaticConstArrays, + Sampler, + Uniform, + Static, + Argument_I, + Argument_O, + Argument_IO +} scope_t; + +typedef enum +{ + Initialized, + Uninitialized +} Kind; + +typedef enum +{ + Read, + Write, + ReadWrite +} Dir; + +// Scope of a name. +class Scope +{ +public: + scope_t scope; + unsigned fnc_idx; + + Scope (scope_t x) ; + Scope (scope_t x, unsigned fnc_idx) ; + + bool eq (const Scope&other) const; + string name () const; + string short_name () const; + bool visible_in (const class Context & ctx, Dir d) const; +}; + +// Declarations - scope, type and name. +class Decl +{ + static unsigned counter; + +public: + Scope scope; + Type type; + ENode initializer; + + // use the creation index as the root of the name + unsigned idx; + + // certain variables should be made unavailable for general assignments + // - for example, loop counters are modified by special + // increment/decrement statements and should not be modified elsewhere + bool noWrites; + + Decl (Scope, Type type); + Decl (Scope, Type type, ENode init); + Decl (Scope, Type type, unsigned idx); + Decl (bool); + Decl (); // default ctor for use with STL containers + bool same (); + static void reset (); +}; + +// A context passes information about what we're doing down through the +// recursive decent generator + +class Context +{ +public: + unsigned func; + ForSPtr loop; + unsigned depth; + bool relop_p; + + UList *samplers; + FunctionList *callees; + + Context relop () const; + Context deeper () const; + + Context (unsigned _owner, + ForSPtr _loop, + UList *samplers, + FunctionList *callees); +}; + +class Gen +{ +private: + friend class Exp; + typedef enum { TokFor, TokWhile, TokDo, TokAssign, + TokSelfMod, TokReturn, TokIf, TokBlock, + TokClose, TokBreak } Token; + typedef std::vector <Token> TokenVec; + Rand rand; + class Knobs & knobs; + std::vector < std::pair < Type, std::string > > uniforms; + unsigned exp_nodes; + unsigned code_nodes; + unsigned max_funcs; + unsigned n_samplers; + unsigned n_uniforms; + + Program functions; + std::vector <FunctionList> callees; + DeclVec symtable; + + void create_control_structure (); + void create_call_structure (); + void declare_samplers (); + + + Decl gen_array_decl (Type t, const Context &ctx, Dir d); + Decl fetch_decl (Type t,const Context &ctx, Kind k, Dir d); + Decl new_variable (Scope scope, Type type, Kind k, const Context &c); + Decl new_variable (Scope scope, const TypeList & type); + Decl new_initialized_variable (Scope scope, Type type, const Context &c); + Decl new_uninitialized_variable (Scope s, Type c, unsigned idx); + Decl new_uninitialized_variable (Scope s, Type c); + DeclVec::iterator existing_decl (Type t, const Context &ctx, Dir d); + + char rchar (const char *what); + ostream & output_boiler_plate (ostream &out) const; + int coverage (const RangeKnob &knob, IntCoverage *marker); + int coverage (unsigned fidx, const RangeKnob &knob, IntCoverage *marker) ; + unsigned ins_stmts (TokenVec *v, unsigned c, + unsigned n, Token t, + unsigned e, bool break_p); + void output_code (ostream &out, const Program &functions); + void output_declarations (ostream &out, Scope s); + ostream &output_typedefs (ostream &out); + + string gen_fconstant (); + Type random_type (); + + friend ostream & operator << (ostream &out, gspair x); + friend ENode create_expression (Gen *gen, Type rtype, const Context &ctx); +public: + Gen (Knobs & _config); + string generate (class OutputInfo *output); +}; + +} +} + +#endif diff --git a/o3d/compiler/puritan/puritan_assert.cc b/o3d/compiler/puritan/puritan_assert.cc new file mode 100644 index 0000000..0b648af --- /dev/null +++ b/o3d/compiler/puritan/puritan_assert.cc @@ -0,0 +1,51 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "puritan_assert.h" +#include <iostream> +#ifdef __GNUC__ +namespace Salem +{ +namespace Puritan +{ +#endif +void puritan_assert_worker (const char *file, const char *func, unsigned line, const std::string &message) +{ + std::cerr << "Fatal error [" << file << ":" << func << ":" << line << "] " << message << "\n"; + exit (1); +} + +#ifdef __GNUC__ +} +} +#endif + diff --git a/o3d/compiler/puritan/puritan_assert.h b/o3d/compiler/puritan/puritan_assert.h new file mode 100644 index 0000000..0e03e73 --- /dev/null +++ b/o3d/compiler/puritan/puritan_assert.h @@ -0,0 +1,83 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef PURITAN_ASSERT_H +#define PURITAN_ASSERT_H + +#include <string> +#include <sstream> + +#ifndef __GNUC__ + +// MSVC and GCC are different - MSVC needs assert worker to be at the top level +// for boost to compile. GCC does not. I don't know which is correct + +__declspec (noreturn) void puritan_assert_worker (const char *, + const char *, + unsigned, + const std::string &); +namespace Salem { namespace Puritan { +#else +namespace Salem { namespace Puritan { + +void puritan_assert_worker (const char *, + const char *, + unsigned, + const std::string &) __attribute__((noreturn)); + +#endif + + +#define PURITAN_ABORT(message) \ +do { \ + std::ostringstream message_buf; \ + message_buf << message; \ + puritan_assert_worker (__FILE__, \ + __FUNCTION__, \ + __LINE__, \ + message_buf.str()); \ +} while (0) + +#define PURITAN_ASSERT(check, message) \ +do { \ + if (!(check)) \ + { \ + PURITAN_ABORT(message); \ + } \ +} while (0) + +#undef assert +#define assert(x) PURITAN_ASSERT(x,"") +} +} + +#endif diff --git a/o3d/compiler/puritan/puritan_shared_ptr.h b/o3d/compiler/puritan/puritan_shared_ptr.h new file mode 100644 index 0000000..ce1fa58 --- /dev/null +++ b/o3d/compiler/puritan/puritan_shared_ptr.h @@ -0,0 +1,292 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// A simple reference counted pointer implementation. It is a subset +// of the boost/tr1 shared_ptr class, which is expected to be part of +// the next C++ standard. See section 20.6.6 [util.smartptr] of the +// draft standard for a full description of the standard interface. +// +// The following standard features have been omitted from this implementation: +// - no custom deallocators - uses delete +// - no support for enable_shared_from_this +// - no support for smart pointer casts +// - no support for features that rely on variadic templates +// - not exception-safe +// - no overloaded comparison operators (e.g. operator<). They're +// convenient, but they can be explicitly defined outside the class. +// + +#ifndef UTIL_GTL_PURITAN_SHARED_PTR_H__ +#define UTIL_GTL_PURITAN_SHARED_PTR_H__ + +#include <algorithm> // for swap + +template <typename T> class shared_ptr; +template <typename T> class weak_ptr; + +// This class is an internal implementation detail for shared_ptr. +// +class SharedPtrControlBlock { + template <typename T> friend class shared_ptr; + template <typename T> friend class weak_ptr; + private: + SharedPtrControlBlock() : refcount_(1), weak_count_(1) { } + int refcount_; + int weak_count_; +}; + +template <typename T> +class shared_ptr { + template <typename U> friend class weak_ptr; + public: + typedef T element_type; + + explicit shared_ptr(T* ptr = NULL) + : ptr_(ptr), + control_block_(ptr != NULL ? new SharedPtrControlBlock : NULL) { + } + + // Copy constructor: makes this object a copy of ptr + template <typename U> + shared_ptr(const shared_ptr<U>& ptr) + : ptr_(NULL), + control_block_(NULL) { + Initialize(ptr); + } + // Need non-templated version to prevent the compiler-generated default + shared_ptr(const shared_ptr<T>& ptr) + : ptr_(NULL), + control_block_(NULL) { + Initialize(ptr); + } + + // Assignment operator. Replaces the existing shared_ptr with ptr. + template <typename U> + shared_ptr<T>& operator=(const shared_ptr<U>& ptr) { + if (ptr_ != ptr.ptr_) { + shared_ptr<T> me(ptr); // will hold our previous state to be destroyed. + swap(me); + } + return *this; + } + + // Need non-templated version to prevent the compiler-generated default + shared_ptr<T>& operator=(const shared_ptr<T>& ptr) { + if (ptr_ != ptr.ptr_) { + shared_ptr<T> me(ptr); // will hold our previous state to be destroyed. + swap(me); + } + return *this; + } + + ~shared_ptr() { + if (ptr_ != NULL) { + if (--control_block_->refcount_ == 0) { + delete ptr_; + + // weak_count_ is defined as the number of weak_ptrs that observe + // ptr_, plus 1 if refcount_ is nonzero. + if (--control_block_->weak_count_ == 0) { + delete control_block_; + } + } + } + } + + // Replaces underlying raw pointer with the one passed in. The reference + // count is set to one (or zero if the pointer is NULL) for the pointer + // being passed in and decremented for the one being replaced. + void reset(T* p = NULL) { + if (p != ptr_) { + shared_ptr<T> tmp(p); + tmp.swap(*this); + } + } + + // Exchanges the contents of this with the contents of r. This function + // supports more efficient swapping since it eliminates the need for a + // temporary shared_ptr object. + void swap(shared_ptr<T>& r) { + std::swap(ptr_, r.ptr_); + std::swap(control_block_, r.control_block_); + } + + // The following function is useful for gaining access to the underlying + // pointer when a shared_ptr remains in scope so the reference-count is + // known to be > 0 (e.g. for parameter passing). + T* get() const { + return ptr_; + } + + T& operator*() const { + return *ptr_; + } + + T* operator->() const { + return ptr_; + } + + long use_count() const { + return control_block_ ? control_block_->refcount_ : 1; + } + + bool unique() const { + return use_count() == 1; + } + + private: + // If r is non-empty, initialize *this to share ownership with r, + // increasing the underlying reference count. + // If r is empty, *this remains empty. + // Requires: this is empty, namely this->ptr_ == NULL. + template <typename U> + void Initialize(const shared_ptr<U>& r) { + if (r.control_block_ != NULL) { + ++r.control_block_->refcount_; + + ptr_ = r.ptr_; + control_block_ = r.control_block_; + } + } + + T* ptr_; + SharedPtrControlBlock* control_block_; + + template <typename U> + friend class shared_ptr; +}; + +// Matches the interface of std::swap as an aid to generic programming. +template <typename T> void swap(shared_ptr<T>& r, shared_ptr<T>& s) { + r.swap(s); +} + +// See comments at the top of the file for a description of why this +// class exists, and the draft C++ standard (as of October 2007 the +// latest draft is N2461) for the detailed specification. +template <typename T> +class weak_ptr { + template <typename U> friend class weak_ptr; + public: + typedef T element_type; + + // Create an empty (i.e. already expired) weak_ptr. + weak_ptr() : ptr_(NULL), control_block_(NULL) { } + + // Create a weak_ptr that observes the same object that ptr points + // to. Note that there is no race condition here: we know that the + // control block can't disappear while we're looking at it because + // it is owned by at least one shared_ptr, ptr. + template <typename U> weak_ptr(const shared_ptr<U>& ptr) { + CopyFrom(ptr.ptr_, ptr.control_block_); + } + + // Copy a weak_ptr. The object it points to might disappear, but we + // don't care: we're only working with the control block, and it can't + // disappear while we're looking at because it's owned by at least one + // weak_ptr, ptr. + template <typename U> weak_ptr(const weak_ptr<U>& ptr) { + CopyFrom(ptr.ptr_, ptr.control_block_); + } + + // Need non-templated version to prevent default copy constructor + weak_ptr(const weak_ptr& ptr) { + CopyFrom(ptr.ptr_, ptr.control_block_); + } + + // Destroy the weak_ptr. If no shared_ptr owns the control block, and if + // we are the last weak_ptr to own it, then it can be deleted. Note that + // weak_count_ is defined as the number of weak_ptrs sharing this control + // block, plus 1 if there are any shared_ptrs. We therefore know that it's + // safe to delete the control block when weak_count_ reaches 0, without + // having to perform any additional tests. + ~weak_ptr() { + if (control_block_ != NULL && + (--control_block_->weak_count_) == 0) { + delete control_block_; + } + } + + weak_ptr& operator=(const weak_ptr& ptr) { + if (&ptr != this) { + weak_ptr tmp(ptr); + tmp.swap(*this); + } + return *this; + } + template <typename U> weak_ptr& operator=(const weak_ptr<U>& ptr) { + weak_ptr tmp(ptr); + tmp.swap(*this); + return *this; + } + template <typename U> weak_ptr& operator=(const shared_ptr<U>& ptr) { + weak_ptr tmp(ptr); + tmp.swap(*this); + return *this; + } + + void swap(weak_ptr& ptr) { + ::swap(ptr_, ptr.ptr_); + ::swap(control_block_, ptr.control_block_); + } + + void reset() { + weak_ptr tmp; + tmp.swap(*this); + } + + // Return the number of shared_ptrs that own the object we are observing. + // Note that this number can be 0 (if this pointer has expired). + long use_count() const { + return control_block_ != NULL ? control_block_->refcount_ : 0; + } + + bool expired() const { return use_count() == 0; } + + private: + void CopyFrom(T* ptr, SharedPtrControlBlock* control_block) { + ptr_ = ptr; + control_block_ = control_block; + if (control_block_ != NULL) + ++control_block_->weak_count_; + } + + private: + element_type* ptr_; + SharedPtrControlBlock* control_block_; +}; + +template <typename T> void swap(weak_ptr<T>& r, weak_ptr<T>& s) { + r.swap(s); +} + +#endif // UTIL_GTL_PURITAN_SHARED_PTR_H__ diff --git a/o3d/compiler/puritan/rand.cc b/o3d/compiler/puritan/rand.cc new file mode 100644 index 0000000..7a4775b --- /dev/null +++ b/o3d/compiler/puritan/rand.cc @@ -0,0 +1,150 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#include <cstddef> +#include <iostream> +#include "rand.h" +#include "puritan_assert.h" + +namespace Salem +{ +namespace Puritan +{ +static const unsigned a = 1103515245 ; +static const unsigned b = 12345; + +// Convention: +// If seed passed in is zero, this class will use the strong random number +// generator from crypto API via CryptGenRandom() +// Otherwise fallback to the linear-congruential generator. +Rand::Rand (unsigned seed) : y (seed+1231) +{ + crypt_provider_ = NULL; + available_ = 0; + if (seed == 0) + InitializeProvider(); +} + +unsigned Rand::rnd_uint(void) +{ + unsigned result = 0; + // If there is no provider, use the LCG. + if (crypt_provider_ == NULL) { + y = (a * y + b); + result = y >> 4; + } +#ifdef _WIN32 + // The following code is used for improved random-number generation. + // TODO: Investigate use of openssl, which may do this on all + // platforms, but will still require platform-specific seeding. + else { + // Maintain a cache of random values by getting a large batch + // on each call, since calling CryptGenRandom() is expensive. + // Note: This code is not thread-safe, neither is the original LCG. + // Use InterlockedDecrement/CompareXchg etc. if that has to change. + long chosen_index = --available_; + + if (chosen_index < 0) { + // We are out of values, time to replenish the cache. + ::CryptGenRandom(crypt_provider_, sizeof(cached_numbers_), + reinterpret_cast<BYTE*>(cached_numbers_)); + available_ = kCacheSize; + chosen_index = --available_; + } + + result = cached_numbers_[chosen_index]; + } +#endif + + return result; +} + +double Rand::rnd_flt() +{ + unsigned r = rnd_uint(); + // We don't ask for the full range of random numbers because + // I see different rounding behavior on different machines at the limit. + unsigned mask = 0xffffff; + unsigned div = mask + 1; + double dr = r & mask; + double res = dr / div; + return res; +} + +int Rand::range (int from, int to) +{ + PURITAN_ASSERT (from <= to, "Range is malformed"); + if (from == to) + { + return from; + } + double x = (rnd_flt ()) * (to - from) + from; + int r = static_cast <int>(x); + return r; +} + +unsigned Rand::urange (unsigned from, unsigned to) +{ + return range (from, to); +} + +size_t Rand::srange (size_t from, size_t to) +{ + return range (static_cast <int>(from), + static_cast <int>(to)); +} + +// Return random string from list of strings. +const char *Rand::from_list (const char **list) +{ + unsigned int i; + for (i = 0; list[i]; i++) + ; + unsigned rn = range (0, i); + return list[rn]; +} + +bool Rand::InitializeProvider() { +#ifdef _WIN32 + int result = ::CryptAcquireContext(& crypt_provider_, NULL, NULL, + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT); + return (result != 0); +#else + // If there is no cryptographic PRNG, this is a noop. + return true; +#endif +} + +} +} diff --git a/o3d/compiler/puritan/rand.h b/o3d/compiler/puritan/rand.h new file mode 100644 index 0000000..d5804c0 --- /dev/null +++ b/o3d/compiler/puritan/rand.h @@ -0,0 +1,73 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef PURITAN_RAND_H +#define PURITAN_RAND_H + +// Use a strong RNG if available. +// TODO: Investigate using openssl on all platforms. +#ifdef _WIN32 +#include <windows.h> +#include <wincrypt.h> +#else +typedef void* HCRYPTPROV; +#endif + + +namespace Salem +{ +namespace Puritan +{ + +class Rand +{ +public: + unsigned y; + unsigned rnd_uint(void) ; + Rand(unsigned seed); + double rnd_flt(void); + int range(int lo, int hi); + unsigned urange(unsigned lo, unsigned hi); + size_t srange(size_t lo, size_t hi); + const char *from_list (const char **); + +private: + bool InitializeProvider(); + + HCRYPTPROV crypt_provider_; + static const size_t kCacheSize = 0x1000; + unsigned int cached_numbers_[kCacheSize]; + long available_; +}; +} +} +#endif diff --git a/o3d/compiler/puritan/structure_gen.cc b/o3d/compiler/puritan/structure_gen.cc new file mode 100644 index 0000000..2be25c7 --- /dev/null +++ b/o3d/compiler/puritan/structure_gen.cc @@ -0,0 +1,328 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#include <sstream> +#include "structure_gen.h" +#include "exp_gen.h" +namespace Salem +{ +namespace Puritan +{ +class Context; + +////////////////////////////////////////////////////////////////////// +// Blocks + +void Block::print_code (ostream &out) +{ + print_code_kids (children, out); +} + +void Block::add_child (CodeSPtr x) +{ + children.push_back (x); +} + +////////////////////////////////////////////////////////////////////// +// Functions + +Function::Function(Gen *_gen, + unsigned _idx, + TypeList _ret_type, + DeclList _formals, + bool _standalone, + bool _noinline) + : gen (_gen), + idx (_idx), + ret_type (_ret_type), + formals (_formals), + standalone (_standalone), + noinline (_noinline) +{ + +} + +void Function::output_code (ostream &out) +{ + // main always returns a struct + if (idx == 0) + { + if (standalone) + { + out << "struct "; + } + + out << "PS_OUTPUT main ("; + } + else + { + if (noinline) + { + out << "noinline "; + + } + out << ret_type << " func" << idx << "("; + } + unsigned arg_idx = 0; + + for (DeclList::iterator i = formals.begin(); + i != formals.end(); + i++, arg_idx++) + { + if (arg_idx != 0) + { + out << ", "; + } + + out << (*i).type << " " << (*i); + + if (standalone && idx == 0) + { + out << ":VPOS"; + } + } + + out << ")\n"; + + + ostringstream code; + + for (CodeVec::iterator x = children.begin(); + x != children.end(); + x++) + { + (*x)->print_code (code); + } + + // emit the declarations at the top of the function and then the guts. + out << "{" + << gspair (gen, Scope(Static, idx)) + << code.str() + << "}"; +} + +////////////////////////////////////////////////////////////////////// +/// For loops + +For::For(Decl _counter, int _from, int _to) + : from (_from), to (_to), counter (_counter) +{ + +} + +void For::print_code (ostream &out) +{ + out << "for (" << counter << " = " << from << ";" + << counter << " < " << to <<"; " + << counter << "++)\n"; + print_code_kids (children, out); +} + +////////////////////////////////////////////////////////////////////// +/// Breaks in loops + +Break::Break (ENode _cond) + : cond (_cond) +{ +} + +void Break::print_code (ostream &out) +{ + out << "if (" << cond << ")\n{ break;}\n"; +} + +////////////////////////////////////////////////////////////////////// +/// While loops + +While::While(ENode _cond, + Decl _index, + unsigned _limit) + : cond(_cond), + counter (_index), + limit (_limit) +{ +} + +void While::print_code (ostream &out) +{ + out << counter << " = " << limit << ";\n" + << "while (" << counter << "> 0 &&" + << cond << ")\n" + << "{" << "--" << counter << ";\n"; + + for (CodeVec::iterator x = children.begin(); + x != children.end(); + x++) + { + (*x)->print_code (out); + } + + out << "}"; +} + +////////////////////////////////////////////////////////////////////// +/// Do loops + +Do::Do(ENode _cond, Decl _counter, unsigned _limit) + : cond(_cond), + counter (_counter), + limit (_limit) +{ +} + +void Do::print_code (ostream &out) +{ + out << counter << " = " << limit << ";\n" + << "do {"; + + for (CodeVec::iterator x = children.begin(); + x != children.end(); + x++) + { + (*x)->print_code (out); + } + + out << "--" << counter << ";\n" + << "} while (" << counter << "> 0 &&" + << cond << ");\n"; +} + +////////////////////////////////////////////////////////////////////// +// Ifs + +IfTemplate::IfTemplate(ENode _cond, bool _has_else) + : cond (_cond), + has_else (_has_else), + toggle (false) +{ +} + +void IfTemplate::print_code (ostream &out) +{ + out << "if (" << cond << ")\n"; + + print_code_kids (children, out); + if (has_else) + { + out << "else\n"; + print_code_kids (other_block, out); + } + +} + +void IfTemplate::add_child (CodeSPtr x) +{ + if (toggle && has_else) + { + other_block.push_back (x); + } + else + { + children.push_back (x); + } + + toggle = ! toggle; +} + +////////////////////////////////////////////////////////////////////// +// Return + +Return::Return(EList _returns, Decl _name) + : returns (_returns), + name (_name) +{ +} + +Return::Return(ENode _reta) : name(false) +{ + returns.push_back (_reta); +} + +void Return::print_code (ostream &out) +{ + if (name.type != NoType) + { + const char *ext[] = {".color0", ".color1", ".color2", ".color3" }; + unsigned k = 0; + for (EList::const_iterator i = returns.begin(); + i != returns.end(); + i++, k++) + { + out << name << ext[k] << " = " << *i << ";\n"; + } + out << "return " << name << ";\n"; + } + else + { + out << "return " << returns.front() << ";\n"; + } +} + +////////////////////////////////////////////////////////////////////// +// Assignments + +AssignmentTemplate::AssignmentTemplate(ENode _terms) + : rp (_terms) +{ +} + +void AssignmentTemplate::print_code (ostream &out) +{ + out << rp << ";\n"; +} + + +////////////////////////////////////////////////////////////////////// +// Skeleton base class for all above +Code::Code() +{ +} + +Code::~Code() +{ +} + +void Code::print_code_kids (CodeVec &children, ostream &out) +{ + out << "{"; + for (CodeVec::iterator x = children.begin(); + x != children.end(); + x++) + { + (*x)->print_code (out); + } + out << "}"; +} + +} +} diff --git a/o3d/compiler/puritan/structure_gen.h b/o3d/compiler/puritan/structure_gen.h new file mode 100644 index 0000000..04f4820 --- /dev/null +++ b/o3d/compiler/puritan/structure_gen.h @@ -0,0 +1,204 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef PURITAN_STRUCTUREGEN_H +#define PURITAN_STRUCTUREGEN_H + +#include "puritan.h" + +namespace Salem +{ +namespace Puritan +{ +typedef ::shared_ptr<class Block> BlockSPtr; +typedef ::shared_ptr<class Break> BreakSPtr; +typedef ::shared_ptr<class Code> CodeSPtr; +typedef ::shared_ptr<class Exp> ENode; +typedef ::shared_ptr<class For> ForSPtr; +typedef ::shared_ptr<class Function> FunctionSPtr; +typedef std::vector <CodeSPtr> CodeVec; +typedef std::vector <FunctionSPtr> FunctionVec; + + +class Code +{ + virtual void print_code (std::ostream &out) = 0; + +public: + Code (); + virtual ~Code(); + + void print_code_kids (CodeVec &children, std::ostream &out); + + friend class Function; + friend class While; + friend class Do; +}; + +class Block:public Code +{ + void print_code (std::ostream &out); + +public: + CodeVec children; + + void add_child (CodeSPtr x); +}; + +class Function:public Block +{ +public: + class Gen *gen; + unsigned idx; + TypeList ret_type; + DeclList formals; + UList samplers; + FunctionSPtr calls; + std::string name; + bool standalone; + bool noinline; + + Function(Gen *gen, unsigned _idx, + TypeList ret_type, + DeclList formals, + bool standalone, + bool noinline); + + void new_decl (std::string name, + Scope scope, + FunctionSPtr func, + enum type type, + std::string init); + + void output_code (std::ostream &out); +}; + + + +class For : public Block +{ + + int from; + int to; + + friend class Index; + friend class ConstArrayRef; +public: + + Decl counter; + + static unsigned fidx; + For(Decl counter, int _from, int _to) ; + + void print_code (std::ostream &out); +}; + +class Break: public Code +{ +public: + ::shared_ptr<Exp> cond; + + Break (ENode _cond) ; + void print_code (std::ostream &out); +} +; + + +class While: public Block +{ + ENode cond; + Decl counter; + unsigned limit; +public: + While(ENode _cond, + Decl i, + unsigned limit) ; + + void print_code (std::ostream &out); +}; + + +class Do: public Block +{ + ENode cond; + Decl counter; + unsigned limit; + +public: + Do(ENode _cond, Decl counter, unsigned limit) ; + + void print_code (std::ostream &out); +}; + +class IfTemplate: public Block +{ + ENode cond; + bool has_else; + bool toggle; + CodeVec other_block; + +public: + IfTemplate(ENode _cond, bool has_else); + + void print_code (std::ostream &out); + void add_child (CodeSPtr x); +}; + + + +class Return : public Code +{ +public: + unsigned struct_elements; + EList returns; + Decl name; + + Return(ENode _reta) ; + Return(EList _returns, Decl name) ; + + void print_code (std::ostream &out); +}; + + +class AssignmentTemplate : public Code +{ +public: + ENode rp; + AssignmentTemplate(ENode _terms) ; + void print_code (std::ostream &out); +}; + + +} +} +#endif diff --git a/o3d/compiler/puritan/test_gen.cc b/o3d/compiler/puritan/test_gen.cc new file mode 100644 index 0000000..85916a3 --- /dev/null +++ b/o3d/compiler/puritan/test_gen.cc @@ -0,0 +1,50 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "puritan.h" +#include "test_gen.h" +#include <string> + +namespace Salem +{ +namespace Puritan +{ +string generate (class OutputInfo *output, Knobs & knobs) +{ + class Gen *gen; + gen = new Gen (knobs); + std::string res = gen->generate (output); + delete gen; + return res; +} +} +} diff --git a/o3d/compiler/puritan/test_gen.h b/o3d/compiler/puritan/test_gen.h new file mode 100644 index 0000000..f5c2cf9 --- /dev/null +++ b/o3d/compiler/puritan/test_gen.h @@ -0,0 +1,47 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef PURITAN_TESTGEN_H +#define PURITAN_TESTGEN_H + +#include <string> +#include "knobs.h" +namespace Salem +{ +namespace Puritan +{ +std::string generate(class OutputInfo *info, + class Knobs &knobs); +} +} + +#endif diff --git a/o3d/compiler/technique/Technique.g3pl b/o3d/compiler/technique/Technique.g3pl new file mode 100644 index 0000000..9f71db1 --- /dev/null +++ b/o3d/compiler/technique/Technique.g3pl @@ -0,0 +1,1126 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// Parse HLSL shaders and separate the shader code from the Technique block +// parse the technique block into shader declarations and stateassignments +// using a Parser and Lexer generated by Antlr3.0 +// +// Compile from the command line using: +// +// $ set CLASSPATH=%CLASSPATH%;c:\bin\antlr\antlr-3.1.jar +// $ java org.antlr.Tool Technique.g3pl +// $ cl *.cc *.c /TP -I c:\bin\antlr\libantlr3c-3.1\include /c /nologo +// $ link *.obj /LTCG /out:technique.exe c:\bin\antlr\antlr3c.lib \ +// /NODEFAULTLIB:libcmt.lib /nologo + + +grammar Technique; + +options { + language = C; +} + +@lexer::preincludes { +#include <vector> +#include "base/logging.h" +#include "core/cross/types.h" + +#ifdef OS_WIN +// These need to be defined here so that when antlr includes +// winsock.h, it doesn't barf. For some reason these are undefined by +// some header (and it appears to be a windows header). +#define IN +#define OUT +#endif +} + +@lexer::postinclude { + namespace o3d { + void TechniqueError(pANTLR3_BASE_RECOGNIZER recognizer, + pANTLR3_UINT8 * tokenNames); + } +} + +@lexer::apifuncs +{ + // Install the custom error reporting function. + RECOGNIZER->displayRecognitionError = o3d::TechniqueError; +} + +@lexer::members { + static int toInt(const pANTLR3_STRING string) { + return atoi(reinterpret_cast<const char*>(string->chars)); + } + static o3d::String toStringL(const pANTLR3_STRING string) { + return o3d::String(reinterpret_cast<const char*>(string->chars)); + } + // Removes escape sequences, as well as leading and trailing quotes. + static ANTLR3_STRING* unescape(const pANTLR3_STRING string) { + if (!string || !string->factory) return NULL; + ANTLR3_STRING* s = string->factory->newRaw(string->factory); + const char *f = reinterpret_cast<const char*>(string->chars); + if (!f) return s; + if (*f == '"') f++; + for (; *f; f++) { + if (*f == '\\') { + switch (*++f) { + case '\0': return s; + case 'b': s->addc(s, '\b'); break; + case 't': s->addc(s, '\t'); break; + case 'n': s->addc(s, '\n'); break; + case 'f': s->addc(s, '\f'); break; + case 'r': s->addc(s, '\r'); break; + default: s->addc(s, *f); break; + } + } else if (*f == '"') { + return s; + } else { + s->addc(s, *f); + } + } + return s; + } + static void setFilename(pANTLR3_BASE_RECOGNIZER rec, + const pANTLR3_STRING filename) { + if (filename) { + // We store the filename into the "custom" field in the lexer state, + // which gets copied into each token. If we use the fileName field + // in the stream itself, when the lexer runs ahead of the parser + // (which is often), the file name can be incorrect. By storing it + // in the token itself, the error code can retrieve the filename + // associated with that token. + rec->state->custom = unescape(filename); + } + } +} + +@parser::preincludes { +// NOTE: this header must be included before the "antlr.h" header +// because on Windows it includes <windows.h> which will define <winsock.h> +// before <winsock2.h>, causing a cascade of struct redefinition errors. +#ifdef OS_WIN +// NOTE: disable compiler warning about C function returning a +// struct. This is caused by the Antlr generated functions being declared +// extern "C" but being compiled and used as C++. +#pragma warning( disable: 4190 ) +#endif +#include <vector> +#include "base/logging.h" +#include "base/string_util.h" +#include "core/cross/types.h" +#include "compiler/technique/technique_structures.h" +} + +@parser::includes { +// NOTE: includes that occur after the "antlr3.h" header has been +// declared which therefore can use the Antlr3 datatypes. +} + +@parser::postinclude { + namespace o3d { + void TechniqueError(pANTLR3_BASE_RECOGNIZER recognizer, + pANTLR3_UINT8 * tokenNames); + void TechniqueSetErrorString(String* e); + } +} + +@parser::apifuncs +{ + // Install the custom error reporting function. + RECOGNIZER->displayRecognitionError = o3d::TechniqueError; +} + +@parser::members { + o3d::String *shader_string_; + o3d::TechniqueDeclarationList *technique_list_; + o3d::SamplerStateList *sampler_list_; + o3d::String *error_string_; + // NOTE: the reinterpret casts below are + // to cast from Antlr3's internal ANTLR_UINT8 strings to the UTF8 + // char* that O3D uses. + o3d::String toString(const pANTLR3_STRING string) { + return o3d::String(reinterpret_cast<const char*>(string->chars)); + } + void addString(const pANTLR3_STRING string) { + shader_string_->append(toString(string)); + } + void addText(const pANTLR3_STRING string) { + if (string && string->chars) { + addString(string); + shader_string_->append("\n"); + } + } + void sampler_state_assignment(const pANTLR3_STRING id, + const pANTLR3_STRING value) { + const char* idc = reinterpret_cast<const char *>(id->chars); + if (!base::strcasecmp(idc, "MinFilter")) { + sampler_list_->back().min_filter = toString(value); + } else if (!base::strcasecmp(idc, "MagFilter")) { + sampler_list_->back().mag_filter = toString(value); + } else if (!base::strcasecmp(idc, "MipFilter")) { + sampler_list_->back().mip_filter = toString(value); + } else if (!base::strcasecmp(idc, "AddressU")) { + sampler_list_->back().address_u = toString(value); + } else if (!base::strcasecmp(idc, "AddressV")) { + sampler_list_->back().address_v = toString(value); + } else if (!base::strcasecmp(idc, "AddressW")) { + sampler_list_->back().address_w = toString(value); + } else if (!base::strcasecmp(idc, "BorderColor")) { + sampler_list_->back().border_color = toString(value); + } else if (!base::strcasecmp(idc, "MaxAnisotropy")) { + sampler_list_->back().max_anisotropy = toString(value); + } + } +} + +// rules ----------------------------------------------------------------------- + +// This is the entry rule - zero or more global declarations followed by an +// end-of-file token. +translation_unit [o3d::TechniqueDeclarationList *technique_list, + o3d::SamplerStateList *sampler_list, + o3d::String *shader_string, + o3d::String *error_string] + returns [bool success] +@init { + // On entry, reset the list of error strings. + technique_list_ = technique_list; + sampler_list_ = sampler_list; + shader_string_ = shader_string; + error_string_ = error_string; + o3d::TechniqueSetErrorString(error_string_); +} +@after { + // On exit, set the return value. + success = error_string_->length() == 0; + technique_list_ = NULL; + sampler_list_ = NULL; + shader_string_ = NULL; + error_string_ = NULL; + o3d::TechniqueSetErrorString(NULL); +} + : + ( noise global_declaration )* EOF + ; + +noise + : + ( COMMENT | WHITESPACE | MULTILINE_COMMENT )* { addText($text); } + | LINE_DIRECTIVE + ; + +global_declaration + : (function_storage_class? function_type_specifier IDENTIFIER LPAREN) => function_declaration + { addText($text); } + | sampler_declaration + | texture_declaration + | struct_definition + { addText($text); } + | typedef_definition + { addText($text); } + | var_declaration + { addText($text); } + | technique_definition + ; + +// variables ------------------------------------------- + +var_declaration + : var_storage_class* + var_type_modifier? + var_datatype id_declaration + semantic? + annotation_list? + ('=' initializer)? + var_packoffset? + var_register_bind? + SEMI + ; + +var_storage_class + : EXTERN + | NOINTERPOLATION + | SHARED + | STATIC + | UNIFORM + | VOLATILE + ; + +var_type_modifier + : T_CONST + | ROW_MAJOR + | COLUMN_MAJOR; + +var_datatype + : buffer_type_specifier + | scalar_type_or_string_specifier + | vector_type_specifier + | matrix_type_specifier + | struct_type_specifier + ; + +var_packoffset + : 'packoffset' LPAREN register_name (DOT IDENTIFIER)? RPAREN + ; + +var_register_bind + : COLON register_name + ; + +id_declaration + : IDENTIFIER ( LBRACKET constant_expression RBRACKET )? + ; + +// function -------------------------------------------- + +function_declaration + : function_storage_class? + function_type_specifier IDENTIFIER LPAREN argument_list RPAREN semantic? + function_body (SEMI)? + ; + +function_storage_class + : INLINE // ignoring platform target + ; + +function_type_specifier + : scalar_type_specifier + | vector_type_specifier + | matrix_type_specifier + | struct_type_specifier + | T_VOID + ; + +semantic + : COLON semantic_specifier ; + +param_type_specifier + : scalar_type_specifier + | vector_type_specifier + | matrix_type_specifier + | struct_type_specifier + | string_type_specifier + | sampler_type_specifier + ; + +basic_type_specifier + : scalar_type_specifier + | vector_type_specifier + | matrix_type_specifier + | string_type_specifier + ; + +// typedef --------------------------------------- + +typedef_definition + : TYPEDEF + ; + +// basic datatypes ------------------------------- + +buffer_type_specifier + : (BUFFER '<' var_datatype '>' IDENTIFIER) + ; + +// effects --------------------------------------------------------------------- + +technique_definition +@declarations { + o3d::TechniqueDeclaration technique_decl; +} +@init { + // clear the technique declaration before use + technique_decl.clear(); +} + : TECHNIQUE IDENTIFIER + { + technique_decl.name = toString($IDENTIFIER.text); + } + annotation_list? '{' ( pass[technique_decl] )+ '}' SEMI? + { + technique_list_->push_back(technique_decl); + } + + | TECHNIQUE + { + technique_decl.name = ""; + } + annotation_list? '{' ( pass[technique_decl] )+ '}' SEMI? + { + technique_list_->push_back(technique_decl); + } + ; + +pass [o3d::TechniqueDeclaration& technique_decl] +@declarations { + o3d::PassDeclaration pass_decl; +} +@after { + $technique_decl.pass.push_back(pass_decl); +} + : PASS IDENTIFIER? + { + if ($IDENTIFIER != NULL && $IDENTIFIER.text->chars != NULL) { + pass_decl.name = toString($IDENTIFIER.text); + } else { + *error_string_ += "Bad pass identifier, line "; + *error_string_ += $PASS.line; + } + } + annotation_list? '{' ( state_assignment[pass_decl] )* '}' SEMI? + ; + +state_assignment [o3d::PassDeclaration& pass] + : (VERTEXSHADER) => + x=VERTEXSHADER '=' 'compile' IDENTIFIER fn=variable_or_call_expression + { + $pass.vertex_shader_entry = $fn.identifier; + $pass.vertex_shader_profile = toString($IDENTIFIER.text); + $pass.vertex_shader_arguments = $fn.arglist; + } + SEMI + | (FRAGMENTSHADER) => + x=FRAGMENTSHADER '=' 'compile' IDENTIFIER fn=variable_or_call_expression + { + $pass.fragment_shader_entry = $fn.identifier; + $pass.fragment_shader_profile = toString($IDENTIFIER.text); + $pass.fragment_shader_arguments = $fn.arglist; + } + SEMI + | IDENTIFIER '=' val=primary_expression + { + if ($IDENTIFIER.text->chars == NULL) { + *error_string_ += "Bad state assigment identifier, line "; + *error_string_ += $IDENTIFIER.line; + + } else if ($val.text->chars == NULL) { + *error_string_ = "Bad state assigment value, line "; + *error_string_ += $IDENTIFIER.line; + } else { + o3d::StateAssignment state; + state.name = toString($IDENTIFIER.text); + state.value = toString($val.text); + $pass.state_assignment.push_back(state); + } + } + SEMI + ; + +// data types ------------------------------------------------------------------ + +scalar_type_specifier + : 'bool' + | 'int' + | 'uint' + | 'half' + | FLOAT + | 'double' + ; + +scalar_type_or_string_specifier + : scalar_type_specifier + | string_type_specifier + ; + +vector_type_specifier + : 'bool1' + | 'bool2' + | 'bool3' + | 'bool4' + | 'int1' + | 'int2' + | 'int3' + | 'int4' + | 'uint1' + | 'uint2' + | 'uint3' + | 'uint4' + | 'half1' + | 'half2' + | 'half3' + | 'half4' + | 'float1' + | 'float2' + | 'float3' + | 'float4' + | 'double1' + | 'double2' + | 'double3' + | 'double4' + | VECTOR '<' scalar_type_specifier ',' DECIMAL_LITERAL '>' + ; + +matrix_type_specifier + : 'float1x1' + | 'float1x2' + | 'float1x3' + | 'float1x4' + | 'float2x1' + | 'float2x2' + | 'float2x3' + | 'float2x4' + | 'float3x1' + | 'float3x2' + | 'float3x3' + | 'float3x4' + | 'float4x1' + | 'float4x2' + | 'float4x3' + | 'float4x4' + | MATRIX '<' scalar_type_specifier ',' + DECIMAL_LITERAL ',' DECIMAL_LITERAL '>' + ; + +string_type_specifier + : STRING + ; + +// Sampler declarations ---------------------------- + +sampler_declaration + : sampler_type_specifier id_declaration + { + o3d::SamplerState s; + s.name = toString($id_declaration.text); + sampler_list_->push_back(s); + addString($sampler_type_specifier.text); + shader_string_->append(" "); + addString($id_declaration.text); + shader_string_->append(";\n"); + } + ( '=' 'sampler_state' '{' sampler_state_declaration+ '}' )? SEMI + ; + +sampler_state_declaration + : TEXTURE '=' '<' IDENTIFIER '>' SEMI + { sampler_list_->back().texture = toString($IDENTIFIER.text); } + | TEXTURE '=' '(' IDENTIFIER ')' SEMI + { sampler_list_->back().texture = toString($IDENTIFIER.text); } + | IDENTIFIER '=' initializer SEMI + { sampler_state_assignment($IDENTIFIER.text, $initializer.text); } + ; + +sampler_type_specifier + : 'sampler' + | 'sampler1D' + | 'sampler2D' + | 'sampler3D' + | 'samplerCUBE' + | 'sampler_state' + | 'SamplerComparisonState' | 'samplercomparisonstate' // DX10 only + ; + + // texture declaration ---------------------------- + +texture_declaration + : texture_type_specifier IDENTIFIER semantic? annotation_list? SEMI + ; + +texture_type_specifier + : TEXTURE + | TEXTURE1D + | TEXTURE2D + | TEXTURE3D + | TEXTURECUBE + | TEXTURERECT + ; + + // struct declaration ----------------------------- + +struct_type_specifier + : IDENTIFIER + | ( STRUCT ( IDENTIFIER )? LCURLY ) => struct_definition + | STRUCT IDENTIFIER + ; + +annotation_list + : '<' annotation* '>' + ; + +annotation + : basic_type_specifier IDENTIFIER '=' initializer SEMI + ; + +initializer + : expression + | '{' expression ( ',' expression )* '}' + ; + +register_name + // registers for VS_3_0 + : REGISTER '(' input_register_name | output_register_name ')'; + +input_register_name + : IDENTIFIER DECIMAL_LITERAL + // IDENTIFIER must be v, r, c, b, i, s, o (check semantically) + | IDENTIFIER + // IDENTIFIER must be a0 aL p0 (check semantically) + ; + +output_register_name + : IDENTIFIER + // must be one of: 'oD0', 'oD1', 'oFog', 'oPos', 'oPts', + // 'oT0', 'oT1', 'oT2', 'oT3', 'oT4', 'oT5', 'oT6', 'oT7' + // (check semantically) + ; + +pack_offset + : .+ ; // no idea what this field looks like. + +argument_list + : ( param_declaration ( COMMA param_declaration )* )? + ; + +param_declaration + : param_direction? param_variability? param_type_specifier id_declaration semantic? +// | FUNCTION type_specifier IDENTIFIER + ; + +param_variability + : T_CONST + | UNIFORM + ; + +param_direction + : T_IN + | T_OUT + | T_INOUT + ; + +function_body + : LCURLY ( decl_or_statement )* RCURLY + ; + +decl_or_statement + // We copied the following sub-rule here to expedite the parsing time + // as this is a much more common case than the "Id init_declarator_list" + // case which would normally spend a lot of time in exception handling. + : (lvalue_expression assignment_operator ) => assignment_statement + | ( ( T_CONST )? vector_type_specifier ) => ( T_CONST )? vector_type_specifier init_declarator_list SEMI + | ( ( T_CONST )? scalar_type_specifier ) => ( T_CONST )? scalar_type_specifier init_declarator_list SEMI + | ( ( T_CONST )? matrix_type_specifier ) => ( T_CONST )? matrix_type_specifier init_declarator_list SEMI + | ( STRUCT ( IDENTIFIER )? LCURLY ) => struct_definition ( init_declarator_list )? SEMI + | STRUCT IDENTIFIER init_declarator_list SEMI + | ( IDENTIFIER init_declarator_list ) => IDENTIFIER init_declarator_list SEMI + | statement + ; + +init_declarator_list + : init_declarator ( COMMA init_declarator )* ; + +init_declarator + : declarator ( ASSIGN expression )? ; + +declarator + : IDENTIFIER ( LBRACKET ( constant_expression )? RBRACKET )* + ; + +struct_definition + : STRUCT ( IDENTIFIER )? LCURLY struct_declaration_list RCURLY IDENTIFIER? SEMI + ; + +struct_declaration_list + // We currently don't support nested structs so the field type + // can only be either a scalar or a vector. + : ( struct_interpolation_modifier? + (scalar_type_specifier|vector_type_specifier) IDENTIFIER + (COLON semantic_specifier)? SEMI )+ + ; + +struct_interpolation_modifier // DX10 only + : T_LINEAR + | CENTROID + | NOINTERPOLATION + | NOPERSPECTIVE + ; + +semantic_specifier + : IDENTIFIER + ; + +statement + : ( lvalue_expression assignment_operator ) => assignment_statement + | ( lvalue_expression self_modify_operator ) => post_modify_statement + | pre_modify_statement + | expression_statement + | compound_statement + | selection_statement + | iteration_statement + | jump_statement + | SEMI + ; + +assignment_statement + : lvalue_expression assignment_operator expression SEMI + ; + +pre_modify_statement + : pre_modify_expression SEMI ; + +pre_modify_expression + : self_modify_operator lvalue_expression ; + +post_modify_statement + : post_modify_expression SEMI ; + +post_modify_expression + : lvalue_expression self_modify_operator ; + +self_modify_operator + : PLUS_PLUS | MINUS_MINUS ; + +expression_statement + : expression SEMI ; + +compound_statement + : LCURLY ( + ( IDENTIFIER init_declarator_list) => IDENTIFIER init_declarator_list SEMI + | ( ( T_CONST )? vector_type_specifier ) => ( T_CONST )? vector_type_specifier init_declarator_list SEMI + | ( ( T_CONST )? scalar_type_specifier ) => ( T_CONST )? scalar_type_specifier init_declarator_list SEMI + | ( STRUCT ( IDENTIFIER )? LCURLY ) => struct_definition ( init_declarator_list )? SEMI + | STRUCT IDENTIFIER init_declarator_list SEMI + | statement + )* + RCURLY + ; + +selection_statement + : IF LPAREN expression RPAREN statement ( ELSE statement )? + ; + +iteration_statement + : WHILE LPAREN expression RPAREN statement + | FOR LPAREN assignment_statement + equality_expression SEMI modify_expression RPAREN statement + | DO statement WHILE LPAREN expression RPAREN SEMI + ; + +modify_expression + : (lvalue_expression assignment_operator ) => + lvalue_expression assignment_operator expression + | pre_modify_expression + | post_modify_expression + ; + +jump_statement + : BREAK SEMI + | CONTINUE + | RETURN ( expression )? SEMI + | DISCARD + ; + +expression + : conditional_expression + ; + +assignment_operator + : ASSIGN + | MUL_ASSIGN + | DIV_ASSIGN + | ADD_ASSIGN + | SUB_ASSIGN + | BITWISE_AND_ASSIGN + | BITWISE_OR_ASSIGN + | BITWISE_XOR_ASSIGN + | BITWISE_SHIFTL_ASSIGN + | BITWISE_SHIFTR_ASSIGN + ; + +constant_expression + : (IDENTIFIER) => variable_expression + | literal_value + ; + +conditional_expression + : logical_or_expression ( QUESTION expression COLON conditional_expression )? + ; + +logical_or_expression + : exclusive_or_expression ( OR exclusive_or_expression )* + ; + +// We remove the NOT operator from the unary expression and stick it here +// so that it has a lower precedence than relational operations. +logical_and_expression + : ( NOT )? inclusive_or_expression ( AND ( NOT )? inclusive_or_expression )* + ; + +inclusive_or_expression + : exclusive_or_expression (BITWISE_OR exclusive_or_expression )* + ; + +exclusive_or_expression + : and_expression ( BITWISE_XOR and_expression )* + ; + +and_expression + : equality_expression ( BITWISE_AND equality_expression )* + ; + +equality_expression + : relational_expression ( (EQUAL|NOT_EQUAL) relational_expression )* + ; + +relational_expression + : shift_expression ( (LESS|GREATER|LESSEQUAL|GREATEREQUAL) shift_expression )* + ; + +shift_expression + : additive_expression ( (BITWISE_SHIFTL|BITWISE_SHIFTR) additive_expression )* + ; + +additive_expression + : multiplicative_expression ( (PLUS|MINUS) multiplicative_expression )* + ; + +multiplicative_expression + : cast_expression ( (MUL|DIV|MOD) cast_expression )* + ; + +cast_expression + : LPAREN IDENTIFIER RPAREN postfix_expression + | LPAREN basic_type_specifier RPAREN postfix_expression + | unary_expression + ; + +unary_expression + : (PLUS|MINUS) unary_expression + | postfix_expression + ; + +postfix_expression + : primary_expression ( postfix_suffix )? + ; + +lvalue_expression + : variable_expression ( postfix_suffix )? + ; + +postfix_suffix + // choosing between struct field access or vector swizzling is a semantic choice. + : ( DOT primary_expression )+ + ; + +primary_expression + : constructor + | variable_or_call_expression + | literal_value + | LPAREN expression RPAREN + ; + +variable_expression + : IDENTIFIER ( LBRACKET expression RBRACKET )? + ; + +// Combine variable expression and call expression here to get rid of the +// syntactic predicate we used to use in the primary_expression rule. Using +// predicates results in the parser spending a lot of time in exception +// handling (when lookahead fails). +variable_or_call_expression + returns [o3d::String identifier, o3d::String arglist] + : IDENTIFIER + ( + ( ( LBRACKET expression RBRACKET )? ) + { + $identifier = toString($IDENTIFIER.text); + $arglist = ""; + } + | + ( LPAREN argument_expression_list RPAREN ) + { + $identifier = toString($IDENTIFIER.text); + if ($argument_expression_list.text->chars) { + $arglist = toString($argument_expression_list.text); + } else { + $arglist = ""; + } + } + ) + ; + +constructor + : ( vector_type_specifier | matrix_type_specifier ) + LPAREN expression ( COMMA expression )* RPAREN + ; + +argument_expression_list + : ( expression ( COMMA expression )* )? + ; + +int_literal + : DECIMAL_LITERAL +// | OCT_LITERAL +// | HEX_LITERAL + ; + +literal_value + : DECIMAL_LITERAL + | FLOAT_LITERAL + | STRING_LITERAL+ + | ( T_FALSE | T_TRUE ) + ; + +float_literal + : FLOAT_LITERAL + ; + +// lexical elements ------------------------------------------------------------ + +NOT : '!' ; +NOT_EQUAL : '!=' ; +AND : '&&' ; +LPAREN : '(' ; +RPAREN : ')' ; +MUL : '*' ; +MUL_ASSIGN : '*=' ; +PLUS : '+' ; +PLUS_PLUS : '++' ; +ADD_ASSIGN : '+=' ; +COMMA : ',' ; +MINUS : '-' ; +MINUS_MINUS : '--' ; +SUB_ASSIGN : '-=' ; +DIV : '/' ; +DIV_ASSIGN : '/=' ; +MOD : '%'; +MOD_ASSIGN : '%='; +COLON : ':' ; +SEMI : ';' ; +LESS : '<' ; +LESSEQUAL : '<=' ; +ASSIGN : '=' ; +EQUAL : '==' ; +GREATER : '>' ; +GREATEREQUAL : '>=' ; +QUESTION : '?' ; +LBRACKET : '[' ; +RBRACKET : ']' ; +LCURLY : '{' ; +OR : '||' ; +RCURLY : '}' ; +DOT : '.' ; +BITWISE_NOT : '~'; +BITWISE_SHIFTL : '<<'; +BITWISE_SHIFTR : '>>'; +BITWISE_AND : '&'; +BITWISE_OR : '|'; +BITWISE_XOR : '^'; +BITWISE_SHIFTL_ASSIGN : '<<='; +BITWISE_SHIFTR_ASSIGN : '>>='; +BITWISE_AND_ASSIGN : '&='; +BITWISE_OR_ASSIGN : '|='; +BITWISE_XOR_ASSIGN : '^='; +// keywords ---------------------------- + +BREAK : 'break'; +BUFFER : 'buffer'; +COLUMN_MAJOR : 'column_major'; +CBUFFER : 'cbuffer'; +CENTROID : 'centroid'; +T_CONST : 'const'; +CONTINUE : 'continue'; +DISCARD : 'discard'; +DO : 'do'; +ELSE : 'else'; +EXTERN : 'extern'; +T_FALSE : 'false'; +FLOAT : (('f'|'F')('l'|'L')('o'|'O')('a'|'A')('t'|'T')); +FOR : 'for'; +IF : 'if'; +T_IN : 'in'; +INLINE : 'inline'; +T_INOUT : 'inout'; +T_LINEAR : 'linear'; +MATRIX : ('m'|'M')('a'|'A')('t'|'T')('r'|'R')('i'|'I')('x'|'X'); +NAMESPACE : 'namespace'; +NOINTERPOLATION : 'nointerpolation'; +NOPERSPECTIVE : 'noperspective'; +T_OUT : 'out'; +RETURN : 'return'; +REGISTER : 'register'; +ROW_MAJOR : 'row_major'; +SHARED : 'shared'; +STATEBLOCK : 'stateblock'; +STATEBLOCK_STATE : 'stateblock_state'; +STATIC : 'static'; +STRING : ('s'|'S')('t'|'T')('r'|'R')('i'|'I')('n'|'N')('g'|'G'); +STRUCT : 'struct'; +SWITCH : 'switch'; +TBUFFER : 'tbuffer'; +TEXTURE : + ('t'|'T')('e'|'E')('x'|'X')('t'|'T')('u'|'U')('r'|'R')('e'|'E'); +TEXTURE1D : 'Texture1D'; +TEXTURE1DARRAY : 'Texture1DArray'; +TEXTURE2D : 'Texture2D'; +TEXTURE2DARRAY : 'Texture2DArray'; +TEXTURE2DMS : 'Texture2DMS'; +TEXTURE2DMSARRAY : 'Texture2DMSArray'; +TEXTURE3D : 'Texture3D'; +TEXTURECUBE : 'TextureCUBE'; +TEXTURECUBEARRAY : 'TextureCUBEArray'; +TEXTURERECT : 'TextureRECT'; +T_TRUE : 'true'; +TYPEDEF : 'typedef'; +UNIFORM : 'uniform'; +VECTOR : ('v'|'V')('e'|'E')('c'|'C')('t'|'T')('o'|'O')('r'|'R'); +T_VOID : 'void'; +VOLATILE : 'volatile'; +WHILE : 'while'; + +// sampler state tokens + +PASS : ('p'|'P')('a'|'A')('s'|'S')('s'|'S'); + +TECHNIQUE : ('t'|'T')('e'|'E')('c'|'C')('h'|'H') + ('n'|'N')('i'|'I')('q'|'Q')('u'|'U')('e'|'E') + ; + +VERTEXSHADER + : (('v'|'V')('e'|'E')('r'|'R')('t'|'T')('e'|'E')('x'|'X')) + ((('s'|'S')('h'|'H')('a'|'A')('d'|'D')('e'|'E')('r'|'R')) | + (('p'|'P')('r'|'R')('o'|'O')('g'|'G')('r'|'R')('a'|'A')('m'|'M'))) + ; + +FRAGMENTSHADER + : (('f'|'F')('r'|'R')('a'|'A')('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T') + ('p'|'P')('r'|'R')('o'|'O')('g'|'G')('r'|'R')('a'|'A')('m'|'M')) + | (('p'|'P')('i'|'I')('x'|'X')('e'|'E')('l'|'L') + ('s'|'S')('h'|'H')('a'|'A')('d'|'D')('e'|'E')('r'|'R')) + ; + +RESERVED_WORDS + : (('a'|'A')('S'|'s')('m'|'M')) + | 'asm_fragment' + | 'auto' + | 'case' + | 'catch' + | 'char' + | 'class' + | 'const_cast' + | (('d'|'D')('e'|'E')('c'|'C')('l'|'L')) + | 'default' + | 'delete' + | (('d'|'D')('w'|'W')('o'|'O')('r'|'R')('d'|'D')) + | 'dynamic_cast' + | 'emit' + | 'enum' + | 'explicit' + | 'fixed' + | 'friend' + | 'get' + | 'goto' + | 'interface' + | 'long' + | 'mutable' + | 'new' + | 'operator' + | 'packed' + | (('p'|'P')('i'|'I')('x'|'X')('e'|'E')('l'|'L') + ('f'|'F')('r'|'R')('a'|'A')('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T')) + | 'private' + | 'protected' + | 'public' + | 'reinterpret_cast' + | 'short' + | 'signed' + | 'sizeof' + | 'snorm' + | 'static_cast' + | 'template' + | 'this' + | 'throw' + | 'try' + | 'typeid' + | 'typename' + | 'union' + | 'unorm' + | 'unsigned' + | 'using' + | (('v'|'V')('e'|'E')('r'|'R')('t'|'T')('e'|'E')('x'|'X') + ('f'|'F')('r'|'R')('a'|'A') + ('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T')) + | 'virtual' + ; + +IDENTIFIER + : ('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')* + ; + +DECIMAL_LITERAL + : ('0'..'9')+ + ; + +fragment ESCAPE_SEQUENCE + : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\') + ; + +CHARACTER_LITERAL + : '\'' ( ESCAPE_SEQUENCE | ~('\''|'\\') ) '\'' + ; + +STRING_LITERAL + : '"' ( ESCAPE_SEQUENCE | ~('\\'|'"') )* '"' + ; + +fragment EXPONENT : ('e'|'E') (PLUS | MINUS)? ('0'..'9')+ ; + +fragment FLOATSUFFIX : ('f'|'F'|'h'|'H') ; + +FLOAT_LITERAL + : ('0'..'9')+ '.' ('0'..'9')* (EXPONENT)? (FLOATSUFFIX)? + | '.' ('0'..'9')+ (EXPONENT)? (FLOATSUFFIX)? + ; + +// skipped elements ------------------------------------------------------------ + +LINE_DIRECTIVE + : '#line' (' '|'\t')* line_num=DECIMAL_LITERAL '\r'? '\n' + { INPUT->setLine(INPUT, toInt($line_num.text)); $channel = HIDDEN; } + | '#line' (' '|'\t')* line_num=DECIMAL_LITERAL + (' '|'\t')* file=STRING_LITERAL '\r'? '\n' + { INPUT->setLine(INPUT, toInt($line_num.text)); $channel = HIDDEN; + setFilename(RECOGNIZER, $file.text); } + ; + +WHITESPACE + : (' '|'\r'|'\t'|'\u000C'|'\n') { $channel = HIDDEN; } + ; + +COMMENT + : '//' ~('\n'|'\r')* '\r'? '\n' { $channel = HIDDEN; } + ; + +MULTILINE_COMMENT + : '/*' ( options {greedy=false;} : . )* '*/' { $channel = HIDDEN; } + ; + +// ----------------------------------------------------------------------------- diff --git a/o3d/compiler/technique/build.scons b/o3d/compiler/technique/build.scons new file mode 100644 index 0000000..384c2c4 --- /dev/null +++ b/o3d/compiler/technique/build.scons @@ -0,0 +1,94 @@ +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import os.path + +Import('env') + +env.Append( + CPPPATH = [ + # Include path for generated headers. + env.Dir('$OBJ_ROOT/compiler/technique'), + # Include path for Antlr C runtime headers. + env.Dir('$ANTLRLIBC_DIR/include'), + ], + LIBPATH = [ + env.Dir('$ANTLRLIBC_DIR/lib'), + ], + LIBS = [ + 'antlr3c', + ], +) + +# Define a builder for Antlr grammars. +def TechniqueEmitter(target, source, env): + # TODO this path operation is horrible, please make '.base' work + base_name = os.path.splitext(source[0].abspath)[0] + generated_targets = [env.File(base_name + 'Lexer.c'), + env.File(base_name + 'Lexer.h'), + env.File(base_name + 'Parser.c'), + env.File(base_name + 'Parser.h'), ] + return generated_targets, source + +if env.Bit('windows'): + env.Append( + CCFLAGS=['/Wp64', '/TP'], + ) +else: + env.Append( + CCFLAGS = ['-x', 'c++'], + ) + +# To enable ANTLRworks debugging, replace +# org.antlr.Tool $SOURCE +# below, with +# org.antlr.Tool -debug $SOURCE + +env['BUILDERS']['Antlr'] = Builder( + action = ' '.join([ + '$JAVA_EXE', + '-cp $ANTLR_DIR/antlr-3.1.1.jar', + 'org.antlr.Tool $SOURCE', + '-fo $TARGET.dir']), + emitter = TechniqueEmitter) + +# Generate the parser and lexer files +antlr_output_files = env.Antlr( env.File('Technique.g3pl') ) +# filter out only the C files from the generated files. +antlr_c_files = [f for f in antlr_output_files if f.suffix == '.c'] + +# Compile the resulting C and C++ files as C++ to build a library +inputs = [ + antlr_c_files, + 'technique_error.cc', + 'technique_parser.cc', + 'technique_structures.cc', +] +env.ComponentLibrary('technique', inputs) diff --git a/o3d/compiler/technique/technique_error.cc b/o3d/compiler/technique/technique_error.cc new file mode 100644 index 0000000..7545ac5 --- /dev/null +++ b/o3d/compiler/technique/technique_error.cc @@ -0,0 +1,283 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// Custom error reporting functon for the Technique parser, as described in +// the Antlr3 documention for the C Runtime at +// http://www.antlr.org/api/C/using.html +// +// NOTE: These error functions are only designed to work with 8-bit +// token streams. + +#include "core/cross/types.h" +#include "compiler/technique/technique_error.h" + +namespace o3d { +String* error_string; + +void TechniqueSetErrorString(String* e) { + error_string = e; +} + +void TechniqueError(pANTLR3_BASE_RECOGNIZER recognizer, + pANTLR3_UINT8 * tokenNames) { + // If the error string pointer has not been set, bail now. + if (!error_string) return; + + pANTLR3_PARSER parser; + pANTLR3_STRING token_text; + pANTLR3_EXCEPTION exception; + pANTLR3_COMMON_TOKEN token; + pANTLR3_BASE_TREE theBaseTree; + pANTLR3_COMMON_TREE theCommonTree; + + // Retrieve some info for easy reading. + exception = recognizer->state->exception; + token_text = NULL; + + pANTLR3_COMMON_TOKEN exception_token = + static_cast<pANTLR3_COMMON_TOKEN>(exception->token); + if (exception_token && exception_token->custom) { + // The filename is retrieved from the "custom" field of the token, which + // was put there by the lexer. The lexer gets it from the #line directives + // in the pre-processed output. + pANTLR3_STRING filename = + static_cast<pANTLR3_STRING>(exception_token->custom); + *error_string += reinterpret_cast<char*>(filename->chars); + } else { + if (exception_token && exception_token->type == ANTLR3_TOKEN_EOF) { + *error_string += "End of input"; + } else { + *error_string += "Unknown source"; + } + } + + // Next comes the line number + char buffer[10]; + base::snprintf(buffer, sizeof(buffer), "%d", exception->line); + *error_string += "("; + *error_string += buffer; + *error_string += ")"; + + *error_string += ": Error: "; + *error_string += reinterpret_cast<char*>(exception->message); + + // Find out what part of the system raised the error. + switch (recognizer->type) { + case ANTLR3_TYPE_PARSER: { + // This is a normal Parser error. Prepare the information we have + // available. + parser = (pANTLR3_PARSER)(recognizer->super); + token = (pANTLR3_COMMON_TOKEN)(exception->token); + + *error_string += ", at offset "; + base::snprintf(buffer, sizeof(buffer), "%d", + exception->charPositionInLine); + *error_string += buffer; + if (token != NULL) { + if (token->type == ANTLR3_TOKEN_EOF) { + ANTLR3_FPRINTF(stderr, ", at <EOF>"); + } else { + token_text = token->getText(token); + if (token_text != NULL) { + *error_string += " near \""; + *error_string += reinterpret_cast<const char*>(token_text->chars); + *error_string += "\""; + } + } + } + break; + } + case ANTLR3_TYPE_LEXER: { + *error_string += "lexer error."; + break; + } + case ANTLR3_TYPE_TREE_PARSER: { + // Tree parsers not supported. Exit. + DLOG(FATAL) << "Technique error should never see a Tree Parser."; + return; + } + default: { + // Parser type was not recognised. Exit. + DLOG(FATAL) << "Technique error called by an unknown Parser type."; + return; + } + } + +#if 0 + // Although this function should generally be provided by the + // implementation, this one should be as helpful as possible for grammar + // developers and serve as an example of what you can do with each + // exception type. In general, when you make up your 'real' handler, you + // should debug the routine with all possible errors you expect which will + // then let you be as specific as possible about all circumstances. + // + // Note that in the general case, errors thrown by tree parsers indicate a + // problem with the output of the parser or with the tree grammar + // itself. The job of the parser is to produce a perfect (in traversal + // terms) syntactically correct tree, so errors at that stage should + // really be semantic errors that your own code determines and handles in + // whatever way is appropriate. + switch (exception->type) { + case ANTLR3_UNWANTED_TOKEN_EXCEPTION: + // Indicates that the recognizer was fed a token which seesm to be + // spurious input. We can detect this when the token that follows + // this unwanted token would normally be part of the syntactically + // correct stream. Then we can see that the token we are looking at + // is just something that should not be there and throw this exception. + if (tokenNames == NULL) { + ANTLR3_FPRINTF(stderr, " : Extraneous input..."); + } else { + if (exception->expecting == ANTLR3_TOKEN_EOF) { + ANTLR3_FPRINTF(stderr, " : Extraneous input - expected <EOF>\n"); + } else { + ANTLR3_FPRINTF(stderr, + " : Extraneous input - expected %s ...\n", + tokenNames[exception->expecting]); + } + } + break; + case ANTLR3_MISSING_TOKEN_EXCEPTION: + // Indicates that the recognizer detected that the token we just + // hit would be valid syntactically if preceeded by a particular + // token. Perhaps a missing ';' at line end or a missing ',' in an + // expression list, and such like. + if (tokenNames == NULL) { + ANTLR3_FPRINTF(stderr, + " : Missing token (%d)...\n", + exception->expecting); + } else { + if (exception->expecting == ANTLR3_TOKEN_EOF) { + ANTLR3_FPRINTF(stderr, " : Missing <EOF>\n"); + } else { + ANTLR3_FPRINTF(stderr, + " : Missing %s \n", + tokenNames[exception->expecting]); + } + } + break; + case ANTLR3_RECOGNITION_EXCEPTION: + // Indicates that the recognizer received a token + // in the input that was not predicted. This is the basic exception type + // from which all others are derived. So we assume it was a syntax error. + // You may get this if there are not more tokens and more are needed + // to complete a parse for instance. + ANTLR3_FPRINTF(stderr, " : syntax error...\n"); + break; + case ANTLR3_MISMATCHED_TOKEN_EXCEPTION: + // We were expecting to see one thing and got another. This is the + // most common error if we coudl not detect a missing or unwanted token. + // Here you can spend your efforts to + // derive more useful error messages based on the expected + // token set and the last token and so on. The error following + // bitmaps do a good job of reducing the set that we were looking + // for down to something small. Knowing what you are parsing may be + // able to allow you to be even more specific about an error. + if (tokenNames == NULL) { + ANTLR3_FPRINTF(stderr, " : syntax error...\n"); + } else { + if (exception->expecting == ANTLR3_TOKEN_EOF) { + ANTLR3_FPRINTF(stderr, " : expected <EOF>\n"); + } else { + ANTLR3_FPRINTF(stderr, + " : expected %s ...\n", + tokenNames[exception->expecting]); + } + } + break; + case ANTLR3_NO_VIABLE_ALT_EXCEPTION: + // We could not pick any alt decision from the input given + // so god knows what happened - however when you examine your grammar, + // you should. It means that at the point where the current token occurred + // that the DFA indicates nowhere to go from here. + ANTLR3_FPRINTF(stderr, " : cannot match to any predicted input...\n"); + break; + case ANTLR3_MISMATCHED_SET_EXCEPTION: { + ANTLR3_UINT32 count; + ANTLR3_UINT32 bit; + ANTLR3_UINT32 size; + ANTLR3_UINT32 numbits; + pANTLR3_BITSET errBits; + + // This means we were able to deal with one of a set of + // possible tokens at this point, but we did not see any + // member of that set. + ANTLR3_FPRINTF(stderr, " : unexpected input...\n expected one of : "); + + // What tokens could we have accepted at this point in the + // parse? + count = 0; + errBits = antlr3BitsetLoad(exception->expectingSet); + numbits = errBits->numBits(errBits); + size = errBits->size(errBits); + + if (size > 0) { + // However many tokens we could have dealt with here, it is usually + // not useful to print ALL of the set here. I arbitrarily chose 8 + // here, but you should do whatever makes sense for you of course. + // No token number 0, so look for bit 1 and on. + for (bit = 1; bit < numbits && count < 8 && count < size; bit++) { + // TODO: This doesn't look right - should be asking if the bit is set! + if (tokenNames[bit]) { + ANTLR3_FPRINTF(stderr, + "%%s", count > 0 ? ", " : "", + tokenNames[bit]); + count++; + } + } + ANTLR3_FPRINTF(stderr, "\n"); + } else { + ANTLR3_FPRINTF(stderr, + "Could not work out which set element was missing.\n"); + } + break; + } + case ANTLR3_EARLY_EXIT_EXCEPTION: + // We entered a loop requiring a number of token sequences + // but found a token that ended that sequence earlier than + // we should have done. + ANTLR3_FPRINTF(stderr, " : missing elements...\n"); + break; + default: + // We don't handle any other exceptions here, but you can + // if you wish. If we get an exception that hits this point + // then we are just going to report what we know about the + // token. + ANTLR3_FPRINTF(stderr, " : syntax not recognized...\n"); + break; + } +#endif // end #if 0 + + *error_string += "\n"; + + DLOG(INFO) << "parse error: " << error_string->c_str(); +} +} // namespace o3d diff --git a/o3d/compiler/technique/technique_error.h b/o3d/compiler/technique/technique_error.h new file mode 100644 index 0000000..260856c --- /dev/null +++ b/o3d/compiler/technique/technique_error.h @@ -0,0 +1,60 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// Custom error reporting functions for the Technique parser. +// +// NOTE: These error functions assume they are interacting with with +// 8-bit token streams. + +#ifndef O3D_COMPILER_TECHNIQUE_TECHNIQUE_ERROR_H_ +#define O3D_COMPILER_TECHNIQUE_TECHNIQUE_ERROR_H_ + +#include <build/build_config.h> +#ifdef OS_WIN +// NOTE: disable compiler warning about C function returning a +// struct. This is caused by the Antlr generated functions being declared +// extern "C" but being compiled and used as C++. +#pragma warning(disable: 4190) +#endif + +#include <vector> +#include "TechniqueLexer.h" +#include "TechniqueParser.h" + +namespace o3d { + +void TechniqueError(pANTLR3_BASE_RECOGNIZER recognizer, + pANTLR3_UINT8 * tokenNames); + +} // namespace o3d + +#endif // O3D_COMPILER_TECHNIQUE_TECHNIQUE_ERROR_H_ diff --git a/o3d/compiler/technique/technique_parser.cc b/o3d/compiler/technique/technique_parser.cc new file mode 100644 index 0000000..01a29de --- /dev/null +++ b/o3d/compiler/technique/technique_parser.cc @@ -0,0 +1,228 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// Driver function that sets up the Parser and Lexer for the "Technique" +// grammar and executes the parsing process, returning the results as a +// string and a Technique structure. +// +// Note that the "TechniqueParser.[c|h]" and "TechniqueLexer.[c|h]" files +// are generated in standard C and compiled under C++, but the code +// generator declares all functions as having "extern C" linkage. This may +// lead to compiler warnings about returning "struct" type values from a +// function - valid in C++, invalid under C. + +#include "base/logging.h" +#include "compiler/technique/technique_parser.h" + +namespace o3d { + +// functions ------------------------------------------------------------------- + +// Use the Technique grammar to parse a UTF8 text file from the filesystem. +bool ParseFxFile( + const String& filename, + String *shader_string, + SamplerStateList *sampler_list, + TechniqueDeclarationList *technique_list, + String *error_string) { + // Create the input stream object from a file of UTF8 held in the filesystem. + // The antlr3 library requires a non-const pointer to the filename. + char* temp = const_cast<char*>(filename.c_str()); + pANTLR3_INPUT_STREAM input_stream = antlr3AsciiFileStreamNew( + reinterpret_cast<pANTLR3_UINT8>(temp)); + if (input_stream == NULL) { + DLOG(ERROR) << "Technique: Unable to open file" << filename + << "due to memory allocation failure."; + return false; + } else { + DLOG(INFO) << "Technique: Opened file \"" << filename << "\""; + } + + // Create the language-specific Lexer. + pTechniqueLexer lexer = TechniqueLexerNew(input_stream); + if (lexer == NULL) { + DLOG(ERROR) << "Technique: Unable to create the lexer"; + input_stream->close(input_stream); + return false; + } else { + DLOG(INFO) << "Technique: Created lexer."; + } + + // Create token stream from the Lexer. + pANTLR3_COMMON_TOKEN_STREAM token_stream = + antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, TOKENSOURCE(lexer)); + if (token_stream == NULL) { + DLOG(ERROR) << "Technique: failed to allocate token stream."; + lexer->free(lexer); + input_stream->close(input_stream); + return false; + } else { + DLOG(INFO) << "Technique: Created token stream."; + } + + // Force the token stream to turn off token filtering and pass all + // whitespace and comments through to the parser. + token_stream->discardOffChannel = ANTLR3_FALSE; + + // Create the language Parser. + pTechniqueParser parser = TechniqueParserNew(token_stream); + if (parser == NULL) { + DLOG(ERROR) << "Technique: Out of memory trying to allocate parser"; + token_stream->free(token_stream); + lexer->free(lexer); + input_stream->close(input_stream); + return false; + } else { + DLOG(INFO) << "Technique: Created parser."; + } + + // call the root parsing rule to parse the input stream. + DLOG(INFO) << "Technique: Parsing..."; + shader_string->clear(); + technique_list->clear(); + sampler_list->clear(); + bool return_value = parser->translation_unit(parser, + technique_list, + sampler_list, + shader_string, + error_string); + DLOG(INFO) << "Technique: Finished parsing."; + + DLOG(INFO) << "Technique: Shader string = "; + DLOG(INFO) << *shader_string; + + // Free created objects. + parser->free(parser); + token_stream->free(token_stream); + lexer->free(lexer); + input_stream->close(input_stream); + + // Finished + return return_value; +} + + +// Use the Technique grammar to parse an FX file from an in-memory, +// zero-terminted UTF8 string buffer. +bool ParseFxString( + const String& fx_string, + String *shader_string, + SamplerStateList *sampler_list, + TechniqueDeclarationList *technique_list, + String* error_string) { + // Create a token stream from a zero-terminated memory buffer of uint8. + ANTLR3_UINT32 fx_string_length = + static_cast<ANTLR3_UINT32>(fx_string.length()); + if (fx_string_length == 0) { + DLOG(INFO) << "Technique: fx string has zero length. Skipping."; + return true; + } + + // Create the input stream from the memory buffer. + // The antlr3 stream library requires a non-const pointer to the fx_string. + char* temp_fx_string = const_cast<char*>(fx_string.c_str()); + pANTLR3_INPUT_STREAM input_stream = + antlr3NewAsciiStringInPlaceStream( + reinterpret_cast<pANTLR3_UINT8>(temp_fx_string), + fx_string_length, + NULL); + if (input_stream == NULL) { + DLOG(ERROR) << "Technique: Unable to create input stream from string."; + return false; + } else { + DLOG(INFO) << "Technique: Created input stream from string."; + } + + // Create the language-specific Lexer. + pTechniqueLexer lexer = TechniqueLexerNew(input_stream); + if (lexer == NULL) { + DLOG(ERROR) << "Technique: Unable to create the lexer"; + input_stream->close(input_stream); + return false; + } else { + DLOG(INFO) << "Technique: Created lexer."; + } + + // Create token stream from the Lexer. + pANTLR3_COMMON_TOKEN_STREAM token_stream = + antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, TOKENSOURCE(lexer)); + if (token_stream == NULL) { + DLOG(ERROR) << "Technique: failed to allocate token stream."; + lexer->free(lexer); + input_stream->close(input_stream); + return false; + } else { + DLOG(INFO) << "Technique: Created token stream."; + } + + // Force the token stream to turn off token filtering and pass all + // whitespace and comments through to the parser. + token_stream->discardOffChannel = ANTLR3_FALSE; + + // Create the language Parser. + pTechniqueParser parser = TechniqueParserNew(token_stream); + if (parser == NULL) { + DLOG(ERROR) << "Technique: Out of memory trying to allocate parser"; + token_stream->free(token_stream); + lexer->free(lexer); + input_stream->close(input_stream); + return false; + } else { + DLOG(INFO) << "Technique: Created parser."; + } + + // call the root parsing rule to parse the input stream. + DLOG(INFO) << "Technique: Parsing..."; + shader_string->clear(); + technique_list->clear(); + sampler_list->clear(); + bool return_value = parser->translation_unit(parser, + technique_list, + sampler_list, + shader_string, + error_string); + DLOG(INFO) << "Technique: Finished parsing."; + + DLOG(INFO) << "Technique: Shader string = "; + DLOG(INFO) << *shader_string; + + // Free created objects. + parser->free(parser); + token_stream->free(token_stream); + lexer->free(lexer); + input_stream->close(input_stream); + + // Completed + return return_value; +} + +} // namespace o3d diff --git a/o3d/compiler/technique/technique_parser.h b/o3d/compiler/technique/technique_parser.h new file mode 100644 index 0000000..ef6e96e --- /dev/null +++ b/o3d/compiler/technique/technique_parser.h @@ -0,0 +1,130 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// Parse an HLSL or Cg ".fx" file and break it into: +// a) source code for the shader +// b) structured information from a "technique{}" block, if one exists. +// +// Entry functions initialize the Parser and Lexer for the "Technique" +// grammar and execute the parsing process, returning the results as a +// string and a Technique object. +// +// Note that the two headers "TechniqueLexer.h" and "TechniqueParser.h" are +// automatically generated using the Antlr compiler-generator from the +// grammar file "Technique.g3pl" +// +// TODO: Note that this is a specialized grammar designed for this +// purpose alone, and it does no sanity checking on the HLSL source +// structure or identifiers itself. A more complete HLSL grammar and +// analyser for this task can be found in the directory +// "o3d/compiler/hlsl". + +#ifndef O3D_COMPILER_TECHNIQUE_TECHNIQUE_PARSER_H_ +#define O3D_COMPILER_TECHNIQUE_TECHNIQUE_PARSER_H_ + +#include <build/build_config.h> +#include <vector> +#include "core/cross/types.h" +#include "compiler/technique/technique_structures.h" +#include "compiler/technique/technique_error.h" + +#ifdef OS_WIN +// NOTE: disable compiler warning about C function returning a +// struct caused by the Antlr generated functions being declared extern"C" +// but being compiled and used as C++. +#pragma warning(disable: 4190) +#endif + +// As these headers are automatically generated, their full path has to be +// passed via the build script as each build target will generate the +// headers in a different location. +#include "TechniqueLexer.h" +#include "TechniqueParser.h" + +namespace o3d { + +// typedefs -------------------------------------------------------------------- + +typedef std::vector<TechniqueDeclaration> TechniqueDeclarationList; + + +// functions ------------------------------------------------------------------- + + +// Use the Technique grammar to parse a UTF8 text file from the filing system. +// Inputs: +// filename - a UTF8 filename to send to the filesystem. +// shader_string - pointer to a String object that will be cleared. +// sampler_list - pointer to a vector of SamplerState objects. +// technique_list - pointer to a vector of TechniqueDeclaration objects. +// error_string - pointer to a String object that will be cleared. +// Returns: +// shader_string - pointer to a String object containing the shader source. +// sampler_list - pointer to a vector of SamplerState objects, each filled +// in with fields parsed from the FX file. +// technique_list - pointer to a vector of TechniqueDeclaration objects, each +// filled with fields parsed from the FX file. +// error_string - pointer to a String object containing parse errors, +// if any. +bool ParseFxFile( + const String &filename, + String *shader_string, + SamplerStateList *sampler_list, + TechniqueDeclarationList *technique_list, + String *error_string); + + +// Use the Technique grammar to parse an in-memory string buffer. +// Inputs: +// fx_string - a zero-terminated UTF8 string of the .fx file source. +// shader_string - pointer to a String object that will be cleared. +// sampler_list - pointer to a vector of SamplerState objects. +// technique_list - pointer to a vector of TechniqueDeclaration objects. +// error_string - pointer to a String object that will be cleared. +// Returns: +// shader_string - pointer to a String object containing the shader source. +// sampler_list - pointer to a vector of SamplerState objects, each filled +// in with fields parsed from the FX file. +// technique_list - pointer to a vector of TechniqueDeclaration objects, each +// filled with fields parsed from the FX file. +// error_string - pointer to a String object containing parse errors, +// if any. +bool ParseFxString( + const String &fx_string, + String *shader_string, + SamplerStateList *sampler_list, + TechniqueDeclarationList *technique_list, + String *error_string); + +} // namespace o3d + +#endif // O3D_COMPILER_TECHNIQUE_TECHNIQUE_PARSER_H_ diff --git a/o3d/compiler/technique/technique_parser_test.cc b/o3d/compiler/technique/technique_parser_test.cc new file mode 100644 index 0000000..ea2ca0a --- /dev/null +++ b/o3d/compiler/technique/technique_parser_test.cc @@ -0,0 +1,414 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// Test program to exercise the Technique Antlr grammar using both files and +// in-memory string buffers. + +#include "tests/common/win/testing_common.h" +#include "compiler/technique/technique_parser.h" + +namespace o3d { + +// classes --------------------------------------------------------------------- + +class TechniqueParserTest : public testing::Test { + protected: + virtual void SetUp(); + virtual void TearDown(); + private: +}; + +void TechniqueParserTest::SetUp() { +} + +void TechniqueParserTest::TearDown() { +} + +// globals --------------------------------------------------------------------- + +char simple_fx_source[] = +"float4x4 worldViewProj : WORLDVIEWPROJECTION; \ +void vs(in float4 pos, out float4 opos) { \ + opos = mul(pos, worldViewProj); \ +} \ +float4 fs(): COLOR { \ + return float3(0.33f, 0.57f, 0.10f); \ +} \ +technique t1 { \ + pass p0 { \ + VertexShader = compile vs_2_0 vs(); \ + PixelShader = compile ps_2_0 fs(); \ + } \ +} \ +"; + +char lambert_fx_source[] = +"struct a2v { \ + float4 pos : POSITION; \ + float3 normal : NORMAL; \ +}; \ + \ +struct v2f { \ + float4 pos : POSITION; \ + float3 n : TEXCOORD0; \ + float3 l : TEXCOORD1; \ +}; \ + \ +float4x4 worldViewProj : WorldViewProjection; \ +float4x4 world : World; \ +float4x4 worldIT : WorldInverseTranspose; \ +float3 lightWorldPos; \ +float4 lightColor; \ + \ +v2f vsMain(a2v IN) { \ + v2f OUT; \ + OUT.pos = mul(IN.pos, worldViewProj); \ + OUT.n = mul(float4(IN.normal,0), worldIT).xyz; \ + OUT.l = lightWorldPos-mul(IN.pos, world).xyz; \ + return OUT; \ +} \ + \ +float4 fsMain(v2f IN): COLOR { \ + float3 l=normalize(IN.l); \ + float3 n=normalize(IN.n); \ + float4 litR=lit(dot(n,l),0,0); \ + return emissive+lightColor*(ambient+diffuse*litR.y); \ +} \ + \ +technique { \ + pass p0 { \ + VertexShader = compile vs_2_0 vsMain(); \ + PixelShader = compile ps_2_0 fsMain(); \ + } \ +} \ +"; + +// ----------------------------------------------------------------------------- + +TEST_F(TechniqueParserTest, ParseSimpleFXFromFile) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String filepath = *g_program_path + "/unittest_data/simple.fx"; + EXPECT_TRUE(ParseFxFile(filepath, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_LT(0, static_cast<int>(technique_list.size())); + ASSERT_EQ(1, technique_list.size()); + EXPECT_EQ("t1", technique_list[0].name); + ASSERT_EQ(0, technique_list[0].annotation.size()); + ASSERT_EQ(1, technique_list[0].pass.size()); + EXPECT_EQ("p0", technique_list[0].pass[0].name); + ASSERT_EQ(0, technique_list[0].pass[0].annotation.size()); + EXPECT_EQ("vs", technique_list[0].pass[0].vertex_shader_entry); + EXPECT_EQ("vs_2_0", technique_list[0].pass[0].vertex_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].vertex_shader_arguments); + EXPECT_EQ("fs", technique_list[0].pass[0].fragment_shader_entry); + EXPECT_EQ("ps_2_0", technique_list[0].pass[0].fragment_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].fragment_shader_arguments); + ASSERT_EQ(0, technique_list[0].pass[0].state_assignment.size()); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseSimpleFXFromString) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + EXPECT_TRUE(ParseFxString(simple_fx_source, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_LT(0, static_cast<int>(technique_list.size())); + ASSERT_EQ(1, technique_list.size()); + EXPECT_EQ("t1", technique_list[0].name); + ASSERT_EQ(0, technique_list[0].annotation.size()); + ASSERT_EQ(1, technique_list[0].pass.size()); + EXPECT_EQ("p0", technique_list[0].pass[0].name); + ASSERT_EQ(0, technique_list[0].pass[0].annotation.size()); + EXPECT_EQ("vs", technique_list[0].pass[0].vertex_shader_entry); + EXPECT_EQ("vs_2_0", technique_list[0].pass[0].vertex_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].vertex_shader_arguments); + EXPECT_EQ("fs", technique_list[0].pass[0].fragment_shader_entry); + EXPECT_EQ("ps_2_0", technique_list[0].pass[0].fragment_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].fragment_shader_arguments); + ASSERT_EQ(0, technique_list[0].pass[0].state_assignment.size()); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseLambertFXFromFile) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String filepath = *g_program_path + "/unittest_data/lambert.fx"; + EXPECT_TRUE(ParseFxFile(filepath.c_str(), + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_LT(0, static_cast<int>(technique_list.size())); + ASSERT_EQ(1, technique_list.size()); + EXPECT_EQ("", technique_list[0].name); + ASSERT_EQ(0, technique_list[0].annotation.size()); + ASSERT_EQ(1, technique_list[0].pass.size()); + EXPECT_EQ("p0", technique_list[0].pass[0].name); + ASSERT_EQ(0, technique_list[0].pass[0].annotation.size()); + EXPECT_EQ("vsMain", technique_list[0].pass[0].vertex_shader_entry); + EXPECT_EQ("vs_2_0", technique_list[0].pass[0].vertex_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].vertex_shader_arguments); + EXPECT_EQ("fsMain", technique_list[0].pass[0].fragment_shader_entry); + EXPECT_EQ("ps_2_0", technique_list[0].pass[0].fragment_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].fragment_shader_arguments); + ASSERT_EQ(0, technique_list[0].pass[0].state_assignment.size()); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseLambertFXFromString) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + EXPECT_TRUE(ParseFxString(lambert_fx_source, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_LT(0, static_cast<int>(technique_list.size())); + ASSERT_EQ(1, technique_list.size()); + EXPECT_EQ("", technique_list[0].name); + ASSERT_EQ(0, technique_list[0].annotation.size()); + ASSERT_EQ(1, technique_list[0].pass.size()); + EXPECT_EQ("p0", technique_list[0].pass[0].name); + ASSERT_EQ(0, technique_list[0].pass[0].annotation.size()); + EXPECT_EQ("vsMain", technique_list[0].pass[0].vertex_shader_entry); + EXPECT_EQ("vs_2_0", technique_list[0].pass[0].vertex_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].vertex_shader_arguments); + EXPECT_EQ("fsMain", technique_list[0].pass[0].fragment_shader_entry); + EXPECT_EQ("ps_2_0", technique_list[0].pass[0].fragment_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].fragment_shader_arguments); + ASSERT_EQ(0, technique_list[0].pass[0].state_assignment.size()); + ASSERT_EQ(0, sampler_list.size()); +} + + + +// Test the longer shaders from files ------------------------------------------ + +TEST_F(TechniqueParserTest, ParseNoShaderFXFromFile) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String filepath = *g_program_path + "/unittest_data/noshader.fx"; + EXPECT_TRUE(ParseFxFile(filepath, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_LT(0, static_cast<int>(technique_list.size())); + ASSERT_EQ(1, technique_list.size()); + EXPECT_EQ("t1", technique_list[0].name); + ASSERT_EQ(0, technique_list[0].annotation.size()); + ASSERT_EQ(1, technique_list[0].pass.size()); + EXPECT_EQ("p0", technique_list[0].pass[0].name); + ASSERT_EQ(0, technique_list[0].pass[0].annotation.size()); + EXPECT_EQ("", technique_list[0].pass[0].vertex_shader_entry); + EXPECT_EQ("", technique_list[0].pass[0].vertex_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].vertex_shader_arguments); + EXPECT_EQ("", technique_list[0].pass[0].fragment_shader_entry); + EXPECT_EQ("", technique_list[0].pass[0].fragment_shader_profile); + EXPECT_EQ("", technique_list[0].pass[0].fragment_shader_arguments); + ASSERT_EQ(4, technique_list[0].pass[0].state_assignment.size()); + EXPECT_EQ("ZEnable", technique_list[0].pass[0].state_assignment[0].name); + EXPECT_EQ("true", technique_list[0].pass[0].state_assignment[0].value); + EXPECT_EQ("ZWriteEnable", technique_list[0].pass[0].state_assignment[1].name); + EXPECT_EQ("true", technique_list[0].pass[0].state_assignment[1].value); + EXPECT_EQ("ZFunc", technique_list[0].pass[0].state_assignment[2].name); + EXPECT_EQ("LessEqual", technique_list[0].pass[0].state_assignment[2].value); + EXPECT_EQ("CullMode", technique_list[0].pass[0].state_assignment[3].name); + EXPECT_EQ("None", technique_list[0].pass[0].state_assignment[3].value); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseNoTechniqueFXFromFile) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String filepath = *g_program_path + "/unittest_data/notechnique.fx"; + EXPECT_TRUE(ParseFxFile(filepath, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_EQ(0, technique_list.size()); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseFurFXFromFile) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String filepath = *g_program_path + "/unittest_data/fur.fx"; + EXPECT_TRUE(ParseFxFile(filepath, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + ASSERT_EQ(1, technique_list.size()); + EXPECT_EQ(1, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseShadowMapFXFromFile) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String filepath = *g_program_path + "/unittest_data/shadow_map.fx"; + EXPECT_TRUE(ParseFxFile(filepath, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + ASSERT_EQ(2, technique_list.size()); + EXPECT_EQ(2, sampler_list.size()); +} + + + +// Tests of error cases -------------------------------------------------------- + +TEST_F(TechniqueParserTest, ParseEmptyString) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String empty_fx_source = ""; + EXPECT_TRUE(ParseFxString(empty_fx_source, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_EQ(0, technique_list.size()); + EXPECT_EQ(String(""), shader_source); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseInvalidString) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String invalid_fx_source = "$%^~ This is an invalid shader."; + EXPECT_FALSE(ParseFxString(invalid_fx_source, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_EQ(0, technique_list.size()); + EXPECT_EQ(0, sampler_list.size()); + // TODO: make sure the string was rejected as an invalid HLSL + // program and test the parser errors. +} + +TEST_F(TechniqueParserTest, ParseInvalidFilename) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String filepath = *g_program_path + "/unittest_data/invalid_filename.fx"; + EXPECT_FALSE(ParseFxFile(filepath, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_EQ(technique_list.size(), 0); + EXPECT_EQ(shader_source, String("")); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseInvalidPassIdentifier) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String invalid_pass_identifier_source = "technique { pass pass { } };"; + EXPECT_FALSE(ParseFxString(invalid_pass_identifier_source, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_EQ(1, technique_list.size()); + ASSERT_EQ(1, technique_list[0].pass.size()); + EXPECT_EQ("", technique_list[0].pass[0].name); + EXPECT_EQ(shader_source, String("")); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseInvalidStateName) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + // NOTE: "FragmentShader" should read "FragmentProgram" or "PixelShader". + String invalid_pass_identifier_source = + "technique { pass { FragmentShader = compile ps_2_0 nothing(); } };"; + EXPECT_FALSE(ParseFxString(invalid_pass_identifier_source, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_EQ(1, technique_list.size()); + ASSERT_EQ(1, technique_list[0].pass.size()); + EXPECT_EQ("", technique_list[0].pass[0].name); + EXPECT_EQ(shader_source, String("")); + EXPECT_EQ(0, sampler_list.size()); +} + +TEST_F(TechniqueParserTest, ParseSampler) { + String shader_source, error_string; + TechniqueDeclarationList technique_list; + SamplerStateList sampler_list; + String filepath = *g_program_path + "/unittest_data/sampler_test.fx"; + EXPECT_TRUE(ParseFxFile(filepath, + &shader_source, + &sampler_list, + &technique_list, + &error_string)); + EXPECT_EQ(1, technique_list.size()); + ASSERT_EQ(1, technique_list[0].pass.size()); + ASSERT_EQ(1, sampler_list.size()); + EXPECT_EQ("Tex0", sampler_list[0].texture); + EXPECT_EQ("Linear", sampler_list[0].min_filter); + EXPECT_EQ("Point", sampler_list[0].mag_filter); + EXPECT_EQ("None", sampler_list[0].mip_filter); + EXPECT_EQ("Mirror", sampler_list[0].address_u); + EXPECT_EQ("Wrap", sampler_list[0].address_v); + EXPECT_EQ("Clamp", sampler_list[0].address_w); + EXPECT_EQ("16", sampler_list[0].max_anisotropy); + EXPECT_EQ("float4(1.0, 0.0, 0.0, 1.0)", sampler_list[0].border_color); +} + +} // namespace o3d diff --git a/o3d/compiler/technique/technique_structures.cc b/o3d/compiler/technique/technique_structures.cc new file mode 100644 index 0000000..f32253d --- /dev/null +++ b/o3d/compiler/technique/technique_structures.cc @@ -0,0 +1,87 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <algorithm> +#include "base/logging.h" +#include "base/cross/std_functional.h" +#include "compiler/technique/technique_structures.h" + +namespace o3d { + +void TechniqueDeclaration::clear() { + name.clear(); + annotation.clear(); + pass.clear(); +} + +void TechniqueDeclaration::dump() { + DLOG(INFO) << "Dump of Technique \"" << name << "\""; + DLOG(INFO) << "Technique Annotation count = " << annotation.size(); + std::for_each(annotation.begin(), + annotation.end(), + std::mem_fun_ref(&Annotation::dump)); + DLOG(INFO) << "Pass count = " << pass.size(); + std::for_each(pass.begin(), + pass.end(), + std::mem_fun_ref(&PassDeclaration::dump)); +} + +void PassDeclaration::dump() { + DLOG(INFO) << "Pass \"" << name << "\""; + DLOG(INFO) << "Pass Annotation count = " << annotation.size(); + std::for_each(annotation.begin(), + annotation.end(), + std::mem_fun_ref(&Annotation::dump)); + DLOG(INFO) << "Vertex shader \"" << vertex_shader_entry << "\""; + DLOG(INFO) << "Vertex profile \"" << vertex_shader_profile << "\""; + DLOG(INFO) << "Vertex args \"" << vertex_shader_arguments << "\""; + DLOG(INFO) << "Fragment shader \"" << fragment_shader_entry << "\""; + DLOG(INFO) << "Fragment profile \"" << fragment_shader_profile << "\""; + DLOG(INFO) << "Fragment args \"" << fragment_shader_arguments << "\""; + DLOG(INFO) << "State Assignment count = " << state_assignment.size(); + std::for_each(state_assignment.begin(), + state_assignment.end(), + std::mem_fun_ref(&StateAssignment::dump)); +} + +void StateAssignment::dump() { + DLOG(INFO) << "State assignment name \"" << name << "\""; + DLOG(INFO) << "State assignment value \"" << value << "\""; +} + +void Annotation::dump() { + DLOG(INFO) << "Annotation name \"" << name << "\""; + DLOG(INFO) << "Annotation type \"" << type << "\""; + DLOG(INFO) << "Annotation value \"" << value << "\""; +} + +} // namespace o3d diff --git a/o3d/compiler/technique/technique_structures.h b/o3d/compiler/technique/technique_structures.h new file mode 100644 index 0000000..ec7607a --- /dev/null +++ b/o3d/compiler/technique/technique_structures.h @@ -0,0 +1,132 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef O3D_COMPILER_TECHNIQUE_TECHNIQUE_STRUCTURES_H_ +#define O3D_COMPILER_TECHNIQUE_TECHNIQUE_STRUCTURES_H_ + +#include <vector> +#include "core/cross/types.h" + +namespace o3d { + +// typedefs -------------------------------------------------------------------- + +class TechniqueDeclaration; +class SamplerState; + +typedef std::vector<TechniqueDeclaration> TechniqueDeclarationList; +typedef std::vector<SamplerState> SamplerStateList; + +// classes --------------------------------------------------------------------- + +// A set of simple data classes that hold the structured information +// uncovered by the Technique parser. All values are public as providing +// accessors for each member would be pretty pointless. If a field is +// missing in the parsed FX file the matching field will either hold an +// empty string or have zero entries in the container. +// +// TODO: if it proves necessary, interpret the "value" fields into +// binary form as opposed to the current model of leaving them as strings. + +class Annotation { + public: + Annotation() {} + Annotation(String t, String n, String v) : type(t), name(n), value(v) {} + ~Annotation() {} + void dump(); + + String type; + String name; + String value; +}; + +class StateAssignment { + public: + StateAssignment() {} + StateAssignment(String n, String v) : name(n), value(v) {} + ~StateAssignment() {} + void dump(); + + String name; + String value; +}; + +class PassDeclaration { + public: + PassDeclaration() {} + explicit PassDeclaration(String n) : name(n) {} + ~PassDeclaration() {} + void dump(); + + String name; + std::vector<Annotation> annotation; + String vertex_shader_entry; + String vertex_shader_profile; + String vertex_shader_arguments; + String fragment_shader_entry; + String fragment_shader_profile; + String fragment_shader_arguments; + std::vector<StateAssignment> state_assignment; +}; + +class TechniqueDeclaration { + public: + TechniqueDeclaration() {} + ~TechniqueDeclaration() {} + void clear(); + void dump(); + + String name; + std::vector<Annotation> annotation; + std::vector<PassDeclaration> pass; +}; + +class SamplerState { + public: + SamplerState() {} + ~SamplerState() {} + + String name; + String texture; + String address_u; + String address_v; + String address_w; + String min_filter; + String mag_filter; + String mip_filter; + String border_color; + String max_anisotropy; +}; + +} // namespeace o3d + +#endif // O3D_COMPILER_TECHNIQUE_TECHNIQUE_STRUCTURES_H_ diff --git a/o3d/compiler/technique/test_data/HLSL_declarations.fx b/o3d/compiler/technique/test_data/HLSL_declarations.fx new file mode 100644 index 0000000..55c927a --- /dev/null +++ b/o3d/compiler/technique/test_data/HLSL_declarations.fx @@ -0,0 +1,30 @@ +// Variable declarations ---------------------------- + +extern aa; +nointerpolation ab; +shared ac; +static ad; +uniform ae; +volatile af; + +static uniform nointerpolation ag; +shared extern const column_major ah; + +extern const ai; +shared const aj; +static const ak; +uniform const al; + +static row_major float2x2 am; +uniform column_major float3x2 an; + + + +// Geometry Shader ---------------- + +Buffer<float4> g_Buffer; + +float4 geom_fn() { + float4 bufferdata = g_Buffer.Load(1); + return bufferdata; +} diff --git a/o3d/compiler/technique/test_data/fur.fx b/o3d/compiler/technique/test_data/fur.fx new file mode 100644 index 0000000..6c326a6 --- /dev/null +++ b/o3d/compiler/technique/test_data/fur.fx @@ -0,0 +1,121 @@ +/*****************************************************************************/ +/* */ +/* File: fur.fx */ +/* */ +/*****************************************************************************/ + +// fur.fx + +//---------------------------------------------------------------------------// + +int shellcount = 20; +int shellnumber = 0; + +float FurScale = 0; +float FurLength = 0; +float UVScale = 0; + + +texture FurTexture +< + string TextureType = "2D"; +>; + + +// transformations +float4x4 worldViewProj : WORLDVIEWPROJ; + +//------------------------------------ +struct vertexInput { + float3 position : POSITION; + float3 normal : NORMAL; + //float4 color : COLOR0; + float4 texCoordDiffuse : TEXCOORD0; + float4 tex1 : TEXCOORD1; +}; + +struct vertexOutput { + float4 HPOS : POSITION; + //float4 color : COLOR; + float4 T0 : TEXCOORD0; + float3 normal : TEXCOORD1; + +}; + + +//------------------------------------ +vertexOutput VS_TransformAndTexture(vertexInput IN) +{ + vertexOutput OUT; + + //float3 P = IN.position.xyz; + //float3 P = IN.position.xyz + (IN.normal * (FurDistance * (float)shellnumber)); + + //float3 P = IN.position.xyz + (IN.normal * FurScale * 10.0f) + (IN.normal*FurLength); + //float3 P = IN.position.xyz + (IN.normal * FurScale * 10.0f) + (IN.normal*FurLength); + + float3 P = IN.position.xyz + (IN.normal * FurLength); + + //OUT.T0 = IN.texCoordDiffuse * 2.0f; + //OUT.T0 = IN.tex1 * FurScale; + OUT.T0 = IN.texCoordDiffuse * UVScale; + //OUT.T0 = IN.tex1 / 3.0f; + OUT.HPOS = mul(float4(P, 1.0f), worldViewProj); + //OUT.color = IN.color; + OUT.normal = IN.normal; + return OUT; +} + + +//------------------------------------ +sampler TextureSampler = sampler_state +{ + texture = <FurTexture>; + AddressU = WRAP; + AddressV = WRAP; + AddressW = WRAP; + MIPFILTER = LINEAR; + MINFILTER = LINEAR; + MAGFILTER = LINEAR; +}; + + +//----------------------------------- +float4 PS_Textured( vertexOutput IN): COLOR +{ + float4 diffuseTexture = tex2D( TextureSampler, IN.T0 ); + //float4 diffuseTexture2 = tex2D( TextureSampler2, IN.T0 ); + + //return (float4(furColor.xyz, FurStrength) * diffuseTexture); + return diffuseTexture; + //return float4(0.0f, 1.0f, 1.0f, 0.3f); //rrggbbaa +} + +//----------------------------------- + +technique Fur +{ + pass Shell + < + string script="Draw=Geometry;"; + > + { + VertexShader = compile vs_1_1 VS_TransformAndTexture(); + PixelShader = compile ps_1_3 PS_Textured(); + AlphaBlendEnable = true; + SrcBlend = srcalpha; + //DestBlend = one; + DestBlend = invsrcalpha; + //DestBlend = srccolor; + //DestBlend = invsrccolor; + //DestBlend = srcalpha; + //DestBlend = destalpha; + //DestBlend = invdestalpha; + //DestBlend = destcolor; + //DestBlend = invdestcolor; + + //CullMode = None; + //CullMode = CCW; + } +} + diff --git a/o3d/compiler/technique/test_data/lambert.fx b/o3d/compiler/technique/test_data/lambert.fx new file mode 100644 index 0000000..b3f34b0 --- /dev/null +++ b/o3d/compiler/technique/test_data/lambert.fx @@ -0,0 +1,38 @@ +struct a2v { + float4 pos : POSITION; + float3 normal : NORMAL; +}; + +struct v2f { + float4 pos : POSITION; + float3 n : TEXCOORD0; + float3 l : TEXCOORD1; +}; + +float4x4 worldViewProj : WorldViewProjection; +float4x4 world : World; +float4x4 worldIT : WorldInverseTranspose; +float3 lightWorldPos; +float4 lightColor; + +v2f vsMain(a2v IN) { + v2f OUT; + OUT.pos = mul(IN.pos, worldViewProj); + OUT.n = mul(float4(IN.normal,0), worldIT).xyz; + OUT.l = lightWorldPos-mul(IN.pos, world).xyz; + return OUT; +} + +float4 fsMain(v2f IN): COLOR { + float3 l=normalize(IN.l); + float3 n=normalize(IN.n); + float4 litR=lit(dot(n,l),0,0); + return emissive+lightColor*(ambient+diffuse*litR.y); +} + +technique { + pass p0 { + VertexShader = compile vs_2_0 vsMain(); + PixelShader = compile ps_2_0 fsMain(); + } +} diff --git a/o3d/compiler/technique/test_data/noshader.fx b/o3d/compiler/technique/test_data/noshader.fx new file mode 100644 index 0000000..13a4924 --- /dev/null +++ b/o3d/compiler/technique/test_data/noshader.fx @@ -0,0 +1,8 @@ +technique t1 { + pass p0 { + ZEnable = true; + ZWriteEnable = true; + ZFunc = LessEqual; + CullMode = None; + } +} diff --git a/o3d/compiler/technique/test_data/notechnique.fx b/o3d/compiler/technique/test_data/notechnique.fx new file mode 100644 index 0000000..2201243 --- /dev/null +++ b/o3d/compiler/technique/test_data/notechnique.fx @@ -0,0 +1,7 @@ +float4x4 worldViewProj : WORLDVIEWPROJECTION; +void vs(in float4 pos, out float4 opos) { + opos = mul(pos, worldViewProj); +} +float4 fs(): COLOR { + return float3(0.33f, 0.57f, 0.10f); +} diff --git a/o3d/compiler/technique/test_data/sampler_test.fx b/o3d/compiler/technique/test_data/sampler_test.fx new file mode 100644 index 0000000..12bcc61 --- /dev/null +++ b/o3d/compiler/technique/test_data/sampler_test.fx @@ -0,0 +1,54 @@ +// texture +texture Tex0 : DiffuseMap < + string name = "tiger.bmp"; + string UIName = "Base Texture"; + >; + +// transformations +float4x4 worldViewProj : WORLDVIEWPROJECTION; + +struct VS_OUTPUT +{ + float4 position : POSITION; + float2 texcoord : TEXCOORD0; +}; + +VS_OUTPUT VS(VS_OUTPUT IN) { + VS_OUTPUT Out = (VS_OUTPUT)0; + Out.position = mul(IN.position, worldViewProj); + Out.texcoord = IN.texcoord; + return Out; +} + +sampler Sampler = sampler_state +{ + Texture = (Tex0); + MinFilter = Linear; + MagFilter = Point; + MipFilter = None; + AddressU = Mirror; + AddressV = Wrap; + AddressW = Clamp; + MaxAnisotropy = 16; + BorderColor = float4(1.0, 0.0, 0.0, 1.0); +}; + + +float4 PS(VS_OUTPUT IN) : COLOR +{ + float4 color = tex2D(Sampler, IN.texcoord); + return color ; +} + + +technique DefaultTechnique +{ + pass P0 + { + // shaders + CullMode = None; + VertexShader = compile vs_2_0 VS(); + PixelShader = compile ps_2_0 PS(); + } +} + diff --git a/o3d/compiler/technique/test_data/shadow_map.fx b/o3d/compiler/technique/test_data/shadow_map.fx new file mode 100644 index 0000000..b4f1c88 --- /dev/null +++ b/o3d/compiler/technique/test_data/shadow_map.fx @@ -0,0 +1,170 @@ +// Shader from NVidia example documentation: +// ftp://download.nvidia.com/developer/SDK/Individual_Samples/ +// MEDIA/docPix/docs/FX_Shadowing.pdf + +texture CTex : RENDERCOLORTARGET < + float2 Dimensions = {256,256}; + string Format = "x8b8g8r8" ; + string UIWidget = "None"; +>; + +sampler CSamp = sampler_state { + texture = <CTex>; + AddressU = CLAMP; AddressV = CLAMP; + MipFilter = NONE; + MinFilter = LINEAR; + MagFilter = LINEAR; +}; + +texture DTex : RENDERDEPTHSTENCILTARGET < + float2 Dimensions = {256,256}; + string format = "D24S8_SHADOWMAP"; + string UIWidget = "None"; +>; + +sampler DSamp = sampler_state { + texture = <DTex>; + AddressU = CLAMP; AddressV = CLAMP; + MipFilter = NONE; + MinFilter = LINEAR; + MagFilter = LINEAR; +}; + +float Script : STANDARDSGLOBAL < + string UIWidget = "none"; + string ScriptClass = "sceneorobject"; + string ScriptOrder = "standard"; + string ScriptOutput = "color"; + string Script = "Tech=Technique?Shadowed:Unshadowed;"; +> = 0.8; // version # + +// The following global variables are values to be used +// when clearing color and/or depth buffers +float4 ClearColor < + string UIWidget = "color"; + string UIName = "background"; +> = {0,0,0,0.0}; + +float ClearDepth < + string UIWidget = "none"; +> = 1.0; + +float4 ShadowClearColor < + string UIWidget = "none"; +> = {1,1,1,0.0}; + +void lightingCalc(ShadowingVertexOutput IN, + out float3 litContrib, + out float3 ambiContrib) { + float3 Nn = normalize(IN.WNormal); + float3 Vn = normalize(IN.WView); + Nn = faceforward(Nn,-Vn,Nn); + float falloff = 1.0 / dot(IN.LightVec,IN.LightVec); + float3 Ln = normalize(IN.LightVec); + float3 Hn = normalize(Vn + Ln); + float hdn = dot(Hn,Nn); + float ldn = dot(Ln,Nn); + float4 litVec = lit(ldn,hdn,SpecExpon); + ldn = litVec.y * SpotLightIntensity; + ambiContrib = SurfColor * AmbiLightColor; + float3 diffContrib = SurfColor*(Kd * ldn * SpotLightColor); + float3 specContrib = ((ldn * litVec.z * Ks) * SpotLightColor); + float3 result = diffContrib + specContrib; + float cone = tex2Dproj(SpotSamp,IN.LProj); + litContrib = ((cone*falloff) * result); +} + +float4 useShadowPS(ShadowingVertexOutput IN) : COLOR { + float3 litPart, ambiPart; + lightingCalc(IN,litPart,ambiPart); + float4 shadowed = tex2Dproj(ShadDepthSampler,IN.LProj); + return float4((shadowed.x*litPart)+ambiPart,1); +} + +float4 unshadowedPS(ShadowingVertexOutput IN) : COLOR { + float3 litPart, ambiPart; + lightingCalc(IN,litPart,ambiPart); +} + +technique Unshadowed < + string Script = "Pass=NoShadow;"; +> { + pass NoShadow < + string Script = + "RenderColorTarget0=;" + "RenderDepthStencilTarget=;" + "RenderPort=;" + "ClearSetColor=ClearColor;" + "ClearSetDepth=ClearDepth;" + "ClearDepth=Color;" + "Clear=Depth;" + "Draw=geometry;"; + > { + VertexShader = compile vs_2_0 + shadowUseVS(WorldXf, + WorldITXf, + WorldViewProjXf, + ShadowViewProjXf, + ViewIXf, + ShadBiasXf, + SpotLightPos); + ZEnable = true; + ZWriteEnable = true; + ZFunc = LessEqual; + CullMode = None; + PixelShader = compile ps_2_a unshadowedPS(); + } +} + +technique Shadowed < + string Script = "Pass=MakeShadow;" + "Pass=UseShadow;"; +> { + pass MakeShadow < + string Script = + "RenderColorTarget0=ColorShadMap;" + "RenderDepthStencilTarget=ShadDepthTarget;" + "RenderPort=light0;" + "ClearSetColor=ShadowClearColor;" + "ClearSetDepth=ClearDepth;" + "Clear=Color;" + "Clear=Depth;" + "Draw=geometry;"; + > { + VertexShader = compile vs_2_0 + shadowGenVS(WorldXf, + WorldITXf, + ShadowViewProjXf); + ZEnable = true; + ZWriteEnable = true; + ZFunc = LessEqual; + CullMode = None; + // no pixel shader needed! + } + + pass UseShadow < + string Script = + "RenderColorTarget0=;" + "RenderDepthStencilTarget=;" + " RenderPort=;" + "ClearSetColor=ClearColor;" + "ClearSetDepth=ClearDepth;" + "Clear=Color;" + "Clear=Depth;" + "Draw=geometry;"; + > { + VertexShader = compile vs_2_0 + shadowUseVS(WorldXf, + WorldITXf, + WorldViewProjXf, + ShadowViewProjXf, + ViewIXf, + ShadBiasXf, + SpotLightPos); + ZEnable = true; + ZWriteEnable = true; + ZFunc = LessEqual; + CullMode = None; + PixelShader = compile ps_2_a useShadowPS(); + } +} diff --git a/o3d/compiler/technique/test_data/simple.fx b/o3d/compiler/technique/test_data/simple.fx new file mode 100644 index 0000000..5c707429 --- /dev/null +++ b/o3d/compiler/technique/test_data/simple.fx @@ -0,0 +1,13 @@ +float4x4 worldViewProj : WORLDVIEWPROJECTION; +void vs(in float4 pos, out float4 opos) { + opos = mul(pos, worldViewProj); +} +float4 fs(): COLOR { + return float3(0.33f, 0.57f, 0.10f); +} +technique t1 { + pass p0 { + VertexShader = compile vs_2_0 vs(); + PixelShader = compile ps_2_0 fs(); + } +} |