Global constant variable value evaluation in AST

sorry, the title information is not very specific :rofl:, below is my example:

const vec3 a = vec3(1.0, 2.0, 3.0);
const float b = a.x;
void main() {
}

we have an opengl compiler based on LLVM, vec3 is a vector type containing 3 float numbers, as you can see, a is a constant value, a.x should be constant value too, it should be 1.0, but when we compile this code, we get below IR:

@a = dso_local addrspace(1) global <3 x float> <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00>, align 16, !dbg !31
@b = dso_local addrspace(1) global float 0.000000e+00, align 4, !dbg !37
@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_example.vert, ptr null }]

define internal  void @__cxx_global_var_init() #0 !dbg !44 {
entry:
  %0 = load <3 x float>, ptr addrspace(1) @a, align 16, !dbg !47
  %1 = extractelement <3 x float> %0, i32 0, !dbg !47
  store float %1, ptr addrspacecast (ptr addrspace(1) @b to ptr), align 4, !dbg !48
  ret void, !dbg !47
}

define dso_local  void @vert_main() #1 !dbg !49 {
entry:
  store float 1.000000e+00, ptr addrspace(1) @gl_PointSize, align 4
  store <4 x float> zeroinitializer, ptr addrspace(1) @gl_Position, align 16
  ret void, !dbg !51
}

Unexpected __cxx_global_var_init function` was generated, below is related AST dump:

|-VarDecl 0x5c389caeab00 </home/triton/work/egp-llvm/build_Debug/lib/clang/17/include/vert.h:4:1, col:45> col:12 gl_Position 'float __attribute__((essl_vector_type(4)))' cinit
| `-InitListExpr 0x5c389caeac90 <col:26, col:45> 'float __attribute__((essl_vector_type(4)))'
|   |-FloatingLiteral 0x5c389caeab70 <col:27> 'float' 0.000000e+00
|   |-FloatingLiteral 0x5c389caeab98 <col:32> 'float' 0.000000e+00
|   |-FloatingLiteral 0x5c389caeabc0 <col:37> 'float' 0.000000e+00
|   `-FloatingLiteral 0x5c389caeabe8 <col:42> 'float' 0.000000e+00
|-VarDecl 0x5c389caead18 <line:5:1, col:30> col:15 gl_PointSize 'float' cinit
| `-FloatingLiteral 0x5c389caead88 <col:30> 'float' 1.000000e+00
|-VarDecl 0x5c389caeadd0 <<source>:1:1, col:28> col:6 used a 'float __attribute__((essl_vector_type(3)))' cinit
| `-CompoundLiteralExpr 0x5c389caeb068 <col:10, col:28> 'float __attribute__((essl_vector_type(3)))'
|   `-InitListExpr 0x5c389caeafa8 <col:14, col:28> 'float __attribute__((essl_vector_type(3)))'
|     |-ConstantExpr 0x5c389caeb008 <col:15> 'float'
|     | `-FloatingLiteral 0x5c389caeae60 <col:15> 'float' 1.000000e+00
|     |-ConstantExpr 0x5c389caeb028 <col:20> 'float'
|     | `-FloatingLiteral 0x5c389caeae88 <col:20> 'float' 2.000000e+00
|     `-ConstantExpr 0x5c389caeb048 <col:25> 'float'
|       `-FloatingLiteral 0x5c389caeaeb0 <col:25> 'float' 3.000000e+00
|-VarDecl 0x5c389caeb0b8 <line:2:1, col:13> col:7 b 'float' cinit
| `-ImplicitCastExpr 0x5c389caeb180 <col:11, col:13> 'float' <LValueToRValue>
|   `-ESSLVectorComponentExpr 0x5c389caeb150 <col:11, col:13> 'float' lvalue vectorcomponent x
|     `-DeclRefExpr 0x5c389caeb128 <col:11> 'float __attribute__((essl_vector_type(3)))' lvalue Var 0x5c389caeadd0 'a' 'float __attribute__((essl_vector_type(3)))'
`-FunctionDecl 0x5c389caeb200 <line:3:1, col:14> col:6 vert_main 'void (void)'
  `-CompoundStmt 0x5c389caeb388 <col:13, col:14>

so we add a custom function like VisitExtVectorComponentExpr

bool LValueExprEvaluator::VisitESSLVectorComponentExpr(const ESSLVectorComponentExpr *E) {
  if (!Visit(E->getBase()))
    return false;

  // čŽ·å–å‘é‡ē»„ä»¶č®æ
  SmallVector<uint32_t, 4> Indices;
  E->getEncodedComponentAccess(Indices);

  APValue BaseVal;
  if (!handleLValueToRValueConversion(Info, E->getBase(),
                                    E->getBase()->getType(), Result, BaseVal))
    return false;

  if (BaseVal.isVector()) {
    if (Indices.size() == 1) {
      BaseVal.dump();
      Result.moveInto(BaseVal.getVectorElt(Indices[0]));
      // Result.set(BaseVal.getVectorElt(Indices[0]).getLValueBase());
      return true;
    } else {
      SmallVector<APValue, 4> Elements;
      for (unsigned I = 0; I < Indices.size(); ++I) {
        Elements.push_back(BaseVal.getVectorElt(Indices[I]));
      }

      APValue VecResult(Elements.data(), Indices.size());
      Result.setFrom(Info.Ctx, VecResult);
      return true;
    }
  }

  return false;
}

but it doesn’t work, any suggestion or idea was appreciatedā¤ļø

1 Like

Not a GLSL expert, but is a actually a constant here? If it is supposed to be one, it doesn’t seem like that information is making it to the AST. Presumably, this is also why the call to Evaluate is failing in your visitor—you’re trying to evaluate something that is not a constant expression.

This is something that I’d expect the optimiser to sort out, not the frontend, e.g. in

int x = 37;
int y = x * 2;

we do initialise y at compile time (Compiler Explorer), but only after optimisation; the frontend emits IR that is quite similar to what you have here (Compiler Explorer).

1 Like

yes, In my example case, b should be constant in compile time, mesa give below results

(
(declare (temporary ) float assignment_tmp)
(declare (temporary ) vec3 assignment_tmp)
(declare (location=0 shader_in ) vec4 gl_Vertex)
(declare (uniform ) (array mat4 0) gl_TextureMatrixInverseTranspose)
(declare (uniform ) (array mat4 0) gl_TextureMatrixTranspose)
(declare (uniform ) mat4 gl_ModelViewProjectionMatrixInverseTranspose)
(declare (uniform ) mat4 gl_ProjectionMatrixInverseTranspose)
(declare (uniform ) mat4 gl_ModelViewMatrixInverseTranspose)
(declare (uniform ) mat4 gl_ModelViewProjectionMatrixTranspose)
(declare (uniform ) mat4 gl_ProjectionMatrixTranspose)
(declare (uniform ) mat4 gl_ModelViewMatrixTranspose)
(declare (uniform ) mat4 gl_ModelViewProjectionMatrix)
(declare (mediump ) vec3 a) (constant vec3 (1.000000 2.000000 3.000000))  (constant vec3 (1.000000 2.000000 3.000000)) 
(declare (mediump ) float b) (constant float (1.000000))  (constant float (1.000000)) 
(assign  (xyz) (var_ref assignment_tmp)  (constant vec3 (1.000000 2.000000 3.000000)) ) 
(assign  (xyz) (var_ref a)  (var_ref assignment_tmp) ) 
(assign  (x) (var_ref assignment_tmp)  (constant float (1.000000)) ) 
(assign  (x) (var_ref b)  (var_ref assignment_tmp) ) 
( function main
  (signature void
    (parameters
    )
    (
    ))

)

)

Looks like ESSLVectorComponentExpr is what a.x refers to, and the constant evaluator doesn’t know about those expressions, so the evaluation will fail.

1 Like

Exactly as what you said, we should add some dealing in visitESSLVectorComponentExpr

Here is another case:

const vec4 v4 = vec4(1.0);
const vec2 v2 = vec2(v4.x, 2.0);
void main(){}

If we use @__cxx_global_var_init() to initial v4, an error will be thrown when we handle v2.

error: initializer element is not a compile-time constant
    2 | const vec2 v2 = vec2(v4.x, 2.0);

I think this error is caused by v4, the value(v4.x = 1.0) determined at run time.
How can I get rid of using __cxx_global_var_init :sob: ?
We attempted to add VisitESSLVectorComponentExpr at class LValueExprEvaluator in the clang/lib/AST/ExprConstant.cpp(All of you can view the above VisitExtVectorComponentExpr function). However, after we invoke getVectorElt(Indices[I]), we only get a APValue, the Result needs a LValue. Has there been a method to convert APValue to LValue. Or how do I resolve this problem :thinking: ? Very much appreciate any help :hand_with_index_finger_and_thumb_crossed:.

Well look at what the existing implementation of VisitExtVectorElementExpr does. There are two implementations of this function in that file, one for primitive return values and one for lvalues.

1 Like

Thanks for your reply! But I do not understand what is one for primitive return values and one for lvalues. I’m so sorry :cry: ! Could you re-explain it?

This implementation: llvm-project/clang/lib/AST/ExprConstant.cpp at 473e2518e850598feae62916ebef4b4dbc88a0ee Ā· llvm/llvm-project Ā· GitHub

returns a primitive value. @zhoujingya already copied this above but didn’t say where it’s implemented, etc.
For the example above, this is basically what you want. Make sure this function (or rather the implementation you write) is called at all for your expression type.

1 Like

You can assume VisitESSLVectorComponentExpr(user-define) is equal to VisitExtVectorElementExpr(They are the same:sweat_smile:). We define the VisitESSLVectorComponentExpr at class LValueExprEvaluator in the clang/lib/AST/ExprConstant.cpp.