]> git.tdb.fi Git - libs/gl.git/blobdiff - source/glsl/spirv.cpp
Use default member initializers for simple types
[libs/gl.git] / source / glsl / spirv.cpp
index be226b3c556ff67650146cb8de9fbcaa2bb188cf..233d15488dd043de95408e3642c075aba9d2d280 100644 (file)
@@ -1,3 +1,4 @@
+#include <msp/core/algorithm.h>
 #include <msp/core/maputils.h>
 #include <msp/core/raii.h>
 #include "reflect.h"
@@ -11,134 +12,138 @@ namespace SL {
 
 const SpirVGenerator::BuiltinFunctionInfo SpirVGenerator::builtin_functions[] =
 {
-       { "radians", "f", "GLSL.std.450", GLSL450_RADIANS, { 1 }, 0 },
-       { "degrees", "f", "GLSL.std.450", GLSL450_DEGREES, { 1 }, 0 },
-       { "sin", "f", "GLSL.std.450", GLSL450_SIN, { 1 }, 0 },
-       { "cos", "f", "GLSL.std.450", GLSL450_COS, { 1 }, 0 },
-       { "tan", "f", "GLSL.std.450", GLSL450_TAN, { 1 }, 0 },
-       { "asin", "f", "GLSL.std.450", GLSL450_ASIN, { 1 }, 0 },
-       { "acos", "f", "GLSL.std.450", GLSL450_ACOS, { 1 }, 0 },
-       { "atan", "f", "GLSL.std.450", GLSL450_ATAN, { 1 }, 0 },
-       { "atan", "ff", "GLSL.std.450", GLSL450_ATAN2, { 1, 2 }, 0 },
-       { "sinh", "f", "GLSL.std.450", GLSL450_SINH, { 1 }, 0 },
-       { "cosh", "f", "GLSL.std.450", GLSL450_COSH, { 1 }, 0 },
-       { "tanh", "f", "GLSL.std.450", GLSL450_TANH, { 1 }, 0 },
-       { "asinh", "f", "GLSL.std.450", GLSL450_ASINH, { 1 }, 0 },
-       { "acosh", "f", "GLSL.std.450", GLSL450_ACOSH, { 1 }, 0 },
-       { "atanh", "f", "GLSL.std.450", GLSL450_ATANH, { 1 }, 0 },
-       { "pow", "ff", "GLSL.std.450", GLSL450_POW, { 1, 2 }, 0 },
-       { "exp", "f", "GLSL.std.450", GLSL450_EXP, { 1 }, 0 },
-       { "log", "f", "GLSL.std.450", GLSL450_LOG, { 1 }, 0 },
-       { "exp2", "f", "GLSL.std.450", GLSL450_EXP2, { 1 }, 0 },
-       { "log2", "f", "GLSL.std.450", GLSL450_LOG2, { 1 }, 0 },
-       { "sqrt", "f", "GLSL.std.450", GLSL450_SQRT, { 1 }, 0 },
-       { "inversesqrt", "f", "GLSL.std.450", GLSL450_INVERSE_SQRT, { 1 }, 0 },
-       { "abs", "f", "GLSL.std.450", GLSL450_F_ABS, { 1 }, 0 },
-       { "abs", "i", "GLSL.std.450", GLSL450_S_ABS, { 1 }, 0 },
-       { "sign", "f", "GLSL.std.450", GLSL450_F_SIGN, { 1 }, 0 },
-       { "sign", "i", "GLSL.std.450", GLSL450_S_SIGN, { 1 }, 0 },
-       { "floor", "f", "GLSL.std.450", GLSL450_FLOOR, { 1 }, 0 },
-       { "trunc", "f", "GLSL.std.450", GLSL450_TRUNC, { 1 }, 0 },
-       { "round", "f", "GLSL.std.450", GLSL450_ROUND, { 1 }, 0 },
-       { "roundEven", "f", "GLSL.std.450", GLSL450_ROUND_EVEN, { 1 }, 0 },
-       { "ceil", "f", "GLSL.std.450", GLSL450_CEIL, { 1 }, 0 },
-       { "fract", "f", "GLSL.std.450", GLSL450_FRACT, { 1 }, 0 },
-       { "mod", "f", "", OP_F_MOD, { 1, 2 }, 0 },
-       { "min", "ff", "GLSL.std.450", GLSL450_F_MIN, { 1, 2 }, 0 },
-       { "min", "ii", "GLSL.std.450", GLSL450_S_MIN, { 1, 2 }, 0 },
-       { "max", "ff", "GLSL.std.450", GLSL450_F_MAX, { 1, 2 }, 0 },
-       { "max", "ii", "GLSL.std.450", GLSL450_S_MAX, { 1, 2 }, 0 },
-       { "clamp", "fff", "GLSL.std.450", GLSL450_F_CLAMP, { 1, 2, 3 }, 0 },
-       { "clamp", "iii", "GLSL.std.450", GLSL450_S_CLAMP, { 1, 2, 3 }, 0 },
-       { "mix", "fff", "GLSL.std.450", GLSL450_F_MIX, { 1, 2, 3 }, 0 },
-       { "mix", "ffb", "", OP_SELECT, { 3, 2, 1 }, 0 },
-       { "mix", "iib", "", OP_SELECT, { 3, 2, 1 }, 0 },
-       { "step", "ff", "GLSL.std.450", GLSL450_F_STEP, { 1, 2 }, 0 },
-       { "smoothstep", "fff", "GLSL.std.450", GLSL450_F_SMOOTH_STEP, { 1, 2, 3 }, 0 },
-       { "isnan", "f", "", OP_IS_NAN, { 1 }, 0 },
-       { "isinf", "f", "", OP_IS_INF, { 1 }, 0 },
-       { "fma", "fff", "GLSL.std.450", GLSL450_F_FMA, { 1, 2, 3 }, 0 },
-       { "length", "f", "GLSL.std.450", GLSL450_LENGTH, { 1 }, 0 },
-       { "distance", "ff", "GLSL.std.450", GLSL450_DISTANCE, { 1, 2 }, 0 },
-       { "dot", "ff", "", OP_DOT, { 1, 2 }, 0 },
-       { "cross", "ff", "GLSL.std.450", GLSL450_CROSS, { 1, 2 }, 0 },
-       { "normalize", "f", "GLSL.std.450", GLSL450_NORMALIZE, { 1 }, 0 },
-       { "faceforward", "fff", "GLSL.std.450", GLSL450_FACE_FORWARD, { 1, 2, 3 }, 0 },
-       { "reflect", "ff", "GLSL.std.450", GLSL450_REFLECT, { 1, 2 }, 0 },
-       { "refract", "fff", "GLSL.std.450", GLSL450_REFRACT, { 1, 2, 3 }, 0 },
-       { "matrixCompMult", "ff", "", 0, { 0 }, &SpirVGenerator::visit_builtin_matrix_comp_mult },
-       { "outerProduct", "ff", "", OP_OUTER_PRODUCT, { 1, 2 }, 0 },
-       { "transpose", "f", "", OP_TRANSPOSE, { 1 }, 0 },
-       { "determinant", "f", "GLSL.std.450", GLSL450_DETERMINANT, { 1 }, 0 },
-       { "inverse", "f", "GLSL.std.450", GLSL450_MATRIX_INVERSE, { 1 }, 0 },
-       { "lessThan", "ff", "", OP_F_ORD_LESS_THAN, { 1, 2 }, 0 },
-       { "lessThan", "ii", "", OP_S_LESS_THAN, { 1, 2 }, 0 },
-       { "lessThanEqual", "ff", "", OP_F_ORD_LESS_THAN_EQUAL, { 1, 2 }, 0 },
-       { "lessThanEqual", "ii", "", OP_S_LESS_THAN_EQUAL, { 1, 2 }, 0 },
-       { "greaterThan", "ff", "", OP_F_ORD_GREATER_THAN, { 1, 2 }, 0 },
-       { "greaterThan", "ii", "", OP_S_GREATER_THAN, { 1, 2 }, 0 },
-       { "greaterThanEqual", "ff", "", OP_F_ORD_GREATER_THAN_EQUAL, { 1, 2 }, 0 },
-       { "greaterThanEqual", "ii", "", OP_S_GREATER_THAN_EQUAL, { 1, 2 }, 0 },
-       { "equal", "ff", "", OP_F_ORD_EQUAL, { 1, 2 }, 0 },
-       { "equal", "ii", "", OP_I_EQUAL, { 1, 2 }, 0 },
-       { "notEqual", "ff", "", OP_F_ORD_NOT_EQUAL, { 1, 2 }, 0 },
-       { "notEqual", "ii", "", OP_I_NOT_EQUAL, { 1, 2 }, 0 },
-       { "any", "b", "", OP_ANY, { 1 }, 0 },
-       { "all", "b", "", OP_ALL, { 1 }, 0 },
-       { "not", "b", "", OP_LOGICAL_NOT, { 1 }, 0 },
-       { "bitfieldExtract", "iii", "", OP_BIT_FIELD_S_EXTRACT, { 1, 2, 3 }, 0 },
-       { "bitfieldInsert", "iiii", "", OP_BIT_FIELD_INSERT, { 1, 2, 3, 4 }, 0 },
-       { "bitfieldReverse", "i", "", OP_BIT_REVERSE, { 1 }, 0 },
-       { "bitCount", "i", "", OP_BIT_COUNT, { 1 }, 0 },
-       { "findLSB", "i", "GLSL.std.450", GLSL450_FIND_I_LSB, { 1 }, 0 },
-       { "findMSB", "i", "GLSL.std.450", GLSL450_FIND_S_MSB, { 1 }, 0 },
-       { "textureSize", "", "", OP_IMAGE_QUERY_SIZE_LOD, { 1, 2 }, 0 },
-       { "texture", "", "", 0, { }, &SpirVGenerator::visit_builtin_texture },
-       { "textureLod", "", "", 0, { }, &SpirVGenerator::visit_builtin_texture },
-       { "texelFetch", "", "", 0, { }, &SpirVGenerator::visit_builtin_texel_fetch },
-       { "EmitVertex", "", "", OP_EMIT_VERTEX, { }, 0 },
-       { "EndPrimitive", "", "", OP_END_PRIMITIVE, { }, 0 },
-       { "dFdx", "f", "", OP_DP_DX, { 1 }, 0 },
-       { "dFdy", "f", "", OP_DP_DY, { 1 }, 0 },
-       { "dFdxFine", "f", "", OP_DP_DX_FINE, { 1 }, 0 },
-       { "dFdyFine", "f", "", OP_DP_DY_FINE, { 1 }, 0 },
-       { "dFdxCoarse", "f", "", OP_DP_DX_COARSE, { 1 }, 0 },
-       { "dFdyCoarse", "f", "", OP_DP_DY_COARSE, { 1 }, 0 },
-       { "fwidth", "f", "", OP_FWIDTH, { 1 }, 0 },
-       { "fwidthFine", "f", "", OP_FWIDTH_FINE, { 1 }, 0 },
-       { "fwidthCoarse", "f", "", OP_FWIDTH_COARSE, { 1 }, 0 },
-       { "interpolateAtCentroid", "", "", 0, { }, &SpirVGenerator::visit_builtin_interpolate },
-       { "interpolateAtSample", "", "", 0, { }, &SpirVGenerator::visit_builtin_interpolate },
-       { "interpolateAtOffset", "", "", 0, { }, &SpirVGenerator::visit_builtin_interpolate },
-       { "", "", "", 0, { }, 0 }
+       { "radians", "f", "GLSL.std.450", GLSL450_RADIANS, { 1 }, 0, 0 },
+       { "degrees", "f", "GLSL.std.450", GLSL450_DEGREES, { 1 }, 0, 0 },
+       { "sin", "f", "GLSL.std.450", GLSL450_SIN, { 1 }, 0, 0 },
+       { "cos", "f", "GLSL.std.450", GLSL450_COS, { 1 }, 0, 0 },
+       { "tan", "f", "GLSL.std.450", GLSL450_TAN, { 1 }, 0, 0 },
+       { "asin", "f", "GLSL.std.450", GLSL450_ASIN, { 1 }, 0, 0 },
+       { "acos", "f", "GLSL.std.450", GLSL450_ACOS, { 1 }, 0, 0 },
+       { "atan", "f", "GLSL.std.450", GLSL450_ATAN, { 1 }, 0, 0 },
+       { "atan", "ff", "GLSL.std.450", GLSL450_ATAN2, { 1, 2 }, 0, 0 },
+       { "sinh", "f", "GLSL.std.450", GLSL450_SINH, { 1 }, 0, 0 },
+       { "cosh", "f", "GLSL.std.450", GLSL450_COSH, { 1 }, 0, 0 },
+       { "tanh", "f", "GLSL.std.450", GLSL450_TANH, { 1 }, 0, 0 },
+       { "asinh", "f", "GLSL.std.450", GLSL450_ASINH, { 1 }, 0, 0 },
+       { "acosh", "f", "GLSL.std.450", GLSL450_ACOSH, { 1 }, 0, 0 },
+       { "atanh", "f", "GLSL.std.450", GLSL450_ATANH, { 1 }, 0, 0 },
+       { "pow", "ff", "GLSL.std.450", GLSL450_POW, { 1, 2 }, 0, 0 },
+       { "exp", "f", "GLSL.std.450", GLSL450_EXP, { 1 }, 0, 0 },
+       { "log", "f", "GLSL.std.450", GLSL450_LOG, { 1 }, 0, 0 },
+       { "exp2", "f", "GLSL.std.450", GLSL450_EXP2, { 1 }, 0, 0 },
+       { "log2", "f", "GLSL.std.450", GLSL450_LOG2, { 1 }, 0, 0 },
+       { "sqrt", "f", "GLSL.std.450", GLSL450_SQRT, { 1 }, 0, 0 },
+       { "inversesqrt", "f", "GLSL.std.450", GLSL450_INVERSE_SQRT, { 1 }, 0, 0 },
+       { "abs", "f", "GLSL.std.450", GLSL450_F_ABS, { 1 }, 0, 0 },
+       { "abs", "i", "GLSL.std.450", GLSL450_S_ABS, { 1 }, 0, 0 },
+       { "sign", "f", "GLSL.std.450", GLSL450_F_SIGN, { 1 }, 0, 0 },
+       { "sign", "i", "GLSL.std.450", GLSL450_S_SIGN, { 1 }, 0, 0 },
+       { "floor", "f", "GLSL.std.450", GLSL450_FLOOR, { 1 }, 0, 0 },
+       { "trunc", "f", "GLSL.std.450", GLSL450_TRUNC, { 1 }, 0, 0 },
+       { "round", "f", "GLSL.std.450", GLSL450_ROUND, { 1 }, 0, 0 },
+       { "roundEven", "f", "GLSL.std.450", GLSL450_ROUND_EVEN, { 1 }, 0, 0 },
+       { "ceil", "f", "GLSL.std.450", GLSL450_CEIL, { 1 }, 0, 0 },
+       { "fract", "f", "GLSL.std.450", GLSL450_FRACT, { 1 }, 0, 0 },
+       { "mod", "f", "", OP_F_MOD, { 1, 2 }, 0, 0 },
+       { "min", "ff", "GLSL.std.450", GLSL450_F_MIN, { 1, 2 }, 0, 0 },
+       { "min", "ii", "GLSL.std.450", GLSL450_S_MIN, { 1, 2 }, 0, 0 },
+       { "min", "uu", "GLSL.std.450", GLSL450_U_MIN, { 1, 2 }, 0, 0 },
+       { "max", "ff", "GLSL.std.450", GLSL450_F_MAX, { 1, 2 }, 0, 0 },
+       { "max", "ii", "GLSL.std.450", GLSL450_S_MAX, { 1, 2 }, 0, 0 },
+       { "max", "uu", "GLSL.std.450", GLSL450_U_MAX, { 1, 2 }, 0, 0 },
+       { "clamp", "fff", "GLSL.std.450", GLSL450_F_CLAMP, { 1, 2, 3 }, 0, 0 },
+       { "clamp", "iii", "GLSL.std.450", GLSL450_S_CLAMP, { 1, 2, 3 }, 0, 0 },
+       { "clamp", "uuu", "GLSL.std.450", GLSL450_U_CLAMP, { 1, 2, 3 }, 0, 0 },
+       { "mix", "fff", "GLSL.std.450", GLSL450_F_MIX, { 1, 2, 3 }, 0, 0 },
+       { "mix", "ffb", "", OP_SELECT, { 3, 2, 1 }, 0, 0 },
+       { "mix", "iib", "", OP_SELECT, { 3, 2, 1 }, 0, 0 },
+       { "mix", "uub", "", OP_SELECT, { 3, 2, 1 }, 0, 0 },
+       { "step", "ff", "GLSL.std.450", GLSL450_F_STEP, { 1, 2 }, 0, 0 },
+       { "smoothstep", "fff", "GLSL.std.450", GLSL450_F_SMOOTH_STEP, { 1, 2, 3 }, 0, 0 },
+       { "isnan", "f", "", OP_IS_NAN, { 1 }, 0, 0 },
+       { "isinf", "f", "", OP_IS_INF, { 1 }, 0, 0 },
+       { "fma", "fff", "GLSL.std.450", GLSL450_F_FMA, { 1, 2, 3 }, 0, 0 },
+       { "length", "f", "GLSL.std.450", GLSL450_LENGTH, { 1 }, 0, 0 },
+       { "distance", "ff", "GLSL.std.450", GLSL450_DISTANCE, { 1, 2 }, 0, 0 },
+       { "dot", "ff", "", OP_DOT, { 1, 2 }, 0, 0 },
+       { "cross", "ff", "GLSL.std.450", GLSL450_CROSS, { 1, 2 }, 0, 0 },
+       { "normalize", "f", "GLSL.std.450", GLSL450_NORMALIZE, { 1 }, 0, 0 },
+       { "faceforward", "fff", "GLSL.std.450", GLSL450_FACE_FORWARD, { 1, 2, 3 }, 0, 0 },
+       { "reflect", "ff", "GLSL.std.450", GLSL450_REFLECT, { 1, 2 }, 0, 0 },
+       { "refract", "fff", "GLSL.std.450", GLSL450_REFRACT, { 1, 2, 3 }, 0, 0 },
+       { "matrixCompMult", "ff", "", 0, { 0 }, 0, &SpirVGenerator::visit_builtin_matrix_comp_mult },
+       { "outerProduct", "ff", "", OP_OUTER_PRODUCT, { 1, 2 }, 0, 0 },
+       { "transpose", "f", "", OP_TRANSPOSE, { 1 }, 0, 0 },
+       { "determinant", "f", "GLSL.std.450", GLSL450_DETERMINANT, { 1 }, 0, 0 },
+       { "inverse", "f", "GLSL.std.450", GLSL450_MATRIX_INVERSE, { 1 }, 0, 0 },
+       { "lessThan", "ff", "", OP_F_ORD_LESS_THAN, { 1, 2 }, 0, 0 },
+       { "lessThan", "ii", "", OP_S_LESS_THAN, { 1, 2 }, 0, 0 },
+       { "lessThan", "uu", "", OP_U_LESS_THAN, { 1, 2 }, 0, 0 },
+       { "lessThanEqual", "ff", "", OP_F_ORD_LESS_THAN_EQUAL, { 1, 2 }, 0, 0 },
+       { "lessThanEqual", "ii", "", OP_S_LESS_THAN_EQUAL, { 1, 2 }, 0, 0 },
+       { "lessThanEqual", "uu", "", OP_U_LESS_THAN_EQUAL, { 1, 2 }, 0, 0 },
+       { "greaterThan", "ff", "", OP_F_ORD_GREATER_THAN, { 1, 2 }, 0, 0 },
+       { "greaterThan", "ii", "", OP_S_GREATER_THAN, { 1, 2 }, 0, 0 },
+       { "greaterThan", "uu", "", OP_U_GREATER_THAN, { 1, 2 }, 0, 0 },
+       { "greaterThanEqual", "ff", "", OP_F_ORD_GREATER_THAN_EQUAL, { 1, 2 }, 0, 0 },
+       { "greaterThanEqual", "ii", "", OP_S_GREATER_THAN_EQUAL, { 1, 2 }, 0, 0 },
+       { "greaterThanEqual", "uu", "", OP_U_GREATER_THAN_EQUAL, { 1, 2 }, 0, 0 },
+       { "equal", "ff", "", OP_F_ORD_EQUAL, { 1, 2 }, 0, 0 },
+       { "equal", "ii", "", OP_I_EQUAL, { 1, 2 }, 0, 0 },
+       { "equal", "uu", "", OP_I_EQUAL, { 1, 2 }, 0, 0 },
+       { "notEqual", "ff", "", OP_F_ORD_NOT_EQUAL, { 1, 2 }, 0, 0 },
+       { "notEqual", "ii", "", OP_I_NOT_EQUAL, { 1, 2 }, 0, 0 },
+       { "notEqual", "uu", "", OP_I_NOT_EQUAL, { 1, 2 }, 0, 0 },
+       { "any", "b", "", OP_ANY, { 1 }, 0, 0 },
+       { "all", "b", "", OP_ALL, { 1 }, 0, 0 },
+       { "not", "b", "", OP_LOGICAL_NOT, { 1 }, 0, 0 },
+       { "bitfieldExtract", "iii", "", OP_BIT_FIELD_S_EXTRACT, { 1, 2, 3 }, 0, 0 },
+       { "bitfieldExtract", "uii", "", OP_BIT_FIELD_U_EXTRACT, { 1, 2, 3 }, 0, 0 },
+       { "bitfieldInsert", "iiii", "", OP_BIT_FIELD_INSERT, { 1, 2, 3, 4 }, 0, 0 },
+       { "bitfieldInsert", "uuii", "", OP_BIT_FIELD_INSERT, { 1, 2, 3, 4 }, 0, 0 },
+       { "bitfieldReverse", "i", "", OP_BIT_REVERSE, { 1 }, 0, 0 },
+       { "bitfieldReverse", "u", "", OP_BIT_REVERSE, { 1 }, 0, 0 },
+       { "bitCount", "i", "", OP_BIT_COUNT, { 1 }, 0, 0 },
+       { "findLSB", "i", "GLSL.std.450", GLSL450_FIND_I_LSB, { 1 }, 0, 0 },
+       { "findLSB", "u", "GLSL.std.450", GLSL450_FIND_I_LSB, { 1 }, 0, 0 },
+       { "findMSB", "i", "GLSL.std.450", GLSL450_FIND_S_MSB, { 1 }, 0, 0 },
+       { "findMSB", "u", "GLSL.std.450", GLSL450_FIND_U_MSB, { 1 }, 0, 0 },
+       { "textureSize", "", "", 0, { }, CAP_IMAGE_QUERY, &SpirVGenerator::visit_builtin_texture_query },
+       { "textureQueryLod", "", "", 0, { }, CAP_IMAGE_QUERY, &SpirVGenerator::visit_builtin_texture_query },
+       { "textureQueryLevels", "", "", 0, { }, CAP_IMAGE_QUERY, &SpirVGenerator::visit_builtin_texture_query },
+       { "texture", "", "", 0, { }, 0, &SpirVGenerator::visit_builtin_texture },
+       { "textureLod", "", "", 0, { }, 0, &SpirVGenerator::visit_builtin_texture },
+       { "texelFetch", "", "", 0, { }, 0, &SpirVGenerator::visit_builtin_texel_fetch },
+       { "EmitVertex", "", "", OP_EMIT_VERTEX, { }, 0, 0 },
+       { "EndPrimitive", "", "", OP_END_PRIMITIVE, { }, 0, 0 },
+       { "dFdx", "f", "", OP_DP_DX, { 1 }, 0, 0 },
+       { "dFdy", "f", "", OP_DP_DY, { 1 }, 0, 0 },
+       { "dFdxFine", "f", "", OP_DP_DX_FINE, { 1 }, CAP_DERIVATIVE_CONTROL, 0 },
+       { "dFdyFine", "f", "", OP_DP_DY_FINE, { 1 }, CAP_DERIVATIVE_CONTROL, 0 },
+       { "dFdxCoarse", "f", "", OP_DP_DX_COARSE, { 1 }, CAP_DERIVATIVE_CONTROL, 0 },
+       { "dFdyCoarse", "f", "", OP_DP_DY_COARSE, { 1 }, CAP_DERIVATIVE_CONTROL, 0 },
+       { "fwidth", "f", "", OP_FWIDTH, { 1 }, 0, 0 },
+       { "fwidthFine", "f", "", OP_FWIDTH_FINE, { 1 }, CAP_DERIVATIVE_CONTROL, 0 },
+       { "fwidthCoarse", "f", "", OP_FWIDTH_COARSE, { 1 }, CAP_DERIVATIVE_CONTROL, 0 },
+       { "interpolateAtCentroid", "", "", 0, { }, CAP_INTERPOLATION_FUNCTION, &SpirVGenerator::visit_builtin_interpolate },
+       { "interpolateAtSample", "", "", 0, { }, CAP_INTERPOLATION_FUNCTION, &SpirVGenerator::visit_builtin_interpolate },
+       { "interpolateAtOffset", "", "", 0, { }, CAP_INTERPOLATION_FUNCTION, &SpirVGenerator::visit_builtin_interpolate },
+       { "", "", "", 0, { }, 0, 0 }
 };
 
 SpirVGenerator::SpirVGenerator():
-       stage(0),
-       current_function(0),
-       writer(content),
-       next_id(1),
-       r_expression_result_id(0),
-       constant_expression(false),
-       spec_constant(false),
-       reachable(false),
-       composite_access(false),
-       r_composite_base_id(0),
-       r_composite_base(0),
-       assignment_source_id(0),
-       loop_merge_block_id(0),
-       loop_continue_target_id(0)
+       writer(content)
 { }
 
 void SpirVGenerator::apply(Module &module)
 {
        use_capability(CAP_SHADER);
 
-       for(list<Stage>::iterator i=module.stages.begin(); i!=module.stages.end(); ++i)
+       for(Stage &s: module.stages)
        {
-               stage = &*i;
+               stage = &s;
                interface_layouts.clear();
-               i->content.visit(*this);
+               s.content.visit(*this);
        }
 
        writer.finalize(SPIRV_GENERATOR_MSP, next_id);
@@ -222,8 +227,28 @@ SpirVGenerator::Id SpirVGenerator::get_id(Node &node) const
 
 SpirVGenerator::Id SpirVGenerator::allocate_id(Node &node, Id type_id)
 {
+       auto i = declared_ids.find(&node);
+       if(i!=declared_ids.end())
+       {
+               if(i->second.type_id)
+                       throw key_error(&node);
+               i->second.type_id = type_id;
+               return i->second.id;
+       }
+
+       Id id = next_id++;
+       declared_ids.insert(make_pair(&node, Declaration(id, type_id)));
+       return id;
+}
+
+SpirVGenerator::Id SpirVGenerator::allocate_forward_id(Node &node)
+{
+       auto i = declared_ids.find(&node);
+       if(i!=declared_ids.end())
+               return i->second.id;
+
        Id id = next_id++;
-       insert_unique(declared_ids, &node, Declaration(id, type_id));
+       declared_ids.insert(make_pair(&node, Declaration(id, 0)));
        return id;
 }
 
@@ -250,6 +275,8 @@ SpirVGenerator::ConstantKey SpirVGenerator::get_constant_key(Id type_id, const V
                return ConstantKey(type_id, value.value<bool>());
        else if(value.check_type<int>())
                return ConstantKey(type_id, value.value<int>());
+       else if(value.check_type<unsigned>())
+               return ConstantKey(type_id, value.value<unsigned>());
        else if(value.check_type<float>())
                return ConstantKey(type_id, value.value<float>());
        else
@@ -281,10 +308,10 @@ SpirVGenerator::Id SpirVGenerator::get_vector_constant_id(Id type_id, unsigned s
        return const_id;
 }
 
-SpirVGenerator::Id SpirVGenerator::get_standard_type_id(BasicTypeDeclaration::Kind kind, unsigned size)
+SpirVGenerator::Id SpirVGenerator::get_standard_type_id(BasicTypeDeclaration::Kind kind, unsigned size, bool sign)
 {
-       Id base_id = (size>1 ? get_standard_type_id(kind, 1) : 0);
-       Id &type_id = standard_type_ids[TypeKey(base_id, (size>1 ? size : static_cast<unsigned>(kind)))];
+       Id base_id = (size>1 ? get_standard_type_id(kind, 1, sign) : 0);
+       Id &type_id = standard_type_ids[base_id ? TypeKey(base_id, size) : TypeKey(kind, sign)];
        if(!type_id)
        {
                type_id = next_id++;
@@ -295,7 +322,7 @@ SpirVGenerator::Id SpirVGenerator::get_standard_type_id(BasicTypeDeclaration::Ki
                else if(kind==BasicTypeDeclaration::BOOL)
                        writer.write_op(content.globals, OP_TYPE_BOOL, type_id);
                else if(kind==BasicTypeDeclaration::INT)
-                       writer.write_op(content.globals, OP_TYPE_INT, type_id, 32, 1);
+                       writer.write_op(content.globals, OP_TYPE_INT, type_id, 32, sign);
                else if(kind==BasicTypeDeclaration::FLOAT)
                        writer.write_op(content.globals, OP_TYPE_FLOAT, type_id, 32);
                else
@@ -306,7 +333,7 @@ SpirVGenerator::Id SpirVGenerator::get_standard_type_id(BasicTypeDeclaration::Ki
 
 bool SpirVGenerator::is_scalar_type(Id type_id, BasicTypeDeclaration::Kind kind) const
 {
-       map<TypeKey, Id>::const_iterator i = standard_type_ids.find(TypeKey(0, kind));
+       auto i = standard_type_ids.find(TypeKey(kind, true));
        return (i!=standard_type_ids.end() && i->second==type_id);
 }
 
@@ -374,7 +401,7 @@ SpirVGenerator::Id SpirVGenerator::get_load_id(VariableDeclaration &var)
 
 void SpirVGenerator::prune_loads(Id min_id)
 {
-       for(map<const VariableDeclaration *, Id>::iterator i=variable_load_ids.begin(); i!=variable_load_ids.end(); )
+       for(auto i=variable_load_ids.begin(); i!=variable_load_ids.end(); )
        {
                if(i->second>=min_id)
                        variable_load_ids.erase(i++);
@@ -394,9 +421,12 @@ SpirVGenerator::Id SpirVGenerator::begin_expression(Opcode opcode, Id type_id, u
                writer.begin_op(content.function_body, opcode, (n_args ? 1+has_result*2+n_args : 0));
        }
        else if(opcode==OP_COMPOSITE_CONSTRUCT)
-               writer.begin_op(content.function_body, OP_SPEC_CONSTANT_COMPOSITE, (n_args ? 1+has_result*2+n_args : 0));
+               writer.begin_op(content.globals, (spec_constant ? OP_SPEC_CONSTANT_COMPOSITE : OP_CONSTANT_COMPOSITE),
+                       (n_args ? 1+has_result*2+n_args : 0));
+       else if(!spec_constant)
+               throw internal_error("invalid non-specialization constant expression");
        else
-               writer.begin_op(content.function_body, OP_SPEC_CONSTANT_OP, (n_args ? 2+has_result*2+n_args : 0));
+               writer.begin_op(content.globals, OP_SPEC_CONSTANT_OP, (n_args ? 2+has_result*2+n_args : 0));
 
        Id result_id = next_id++;
        if(has_result)
@@ -404,7 +434,7 @@ SpirVGenerator::Id SpirVGenerator::begin_expression(Opcode opcode, Id type_id, u
                writer.write(type_id);
                writer.write(result_id);
        }
-       if(constant_expression && opcode!=OP_COMPOSITE_CONSTRUCT)
+       if(spec_constant && opcode!=OP_COMPOSITE_CONSTRUCT)
                writer.write(opcode);
 
        return result_id;
@@ -413,7 +443,7 @@ SpirVGenerator::Id SpirVGenerator::begin_expression(Opcode opcode, Id type_id, u
 void SpirVGenerator::end_expression(Opcode opcode)
 {
        if(constant_expression)
-               opcode = (opcode==OP_COMPOSITE_CONSTRUCT ? OP_SPEC_CONSTANT_COMPOSITE : OP_SPEC_CONSTANT_OP);
+               opcode = (opcode==OP_COMPOSITE_CONSTRUCT ? spec_constant ? OP_SPEC_CONSTANT_COMPOSITE : OP_CONSTANT_COMPOSITE : OP_SPEC_CONSTANT_OP);
        writer.end_op(opcode);
 }
 
@@ -455,20 +485,10 @@ SpirVGenerator::Id SpirVGenerator::write_construct(Id type_id, const Id *elem_id
        return result_id;
 }
 
-BasicTypeDeclaration &SpirVGenerator::get_element_type(BasicTypeDeclaration &basic)
-{
-       if(basic.kind==BasicTypeDeclaration::BOOL || basic.kind==BasicTypeDeclaration::INT || basic.kind==BasicTypeDeclaration::FLOAT)
-               return basic;
-       else if((basic.kind==BasicTypeDeclaration::VECTOR || basic.kind==BasicTypeDeclaration::MATRIX) && basic.base_type)
-               return get_element_type(dynamic_cast<BasicTypeDeclaration &>(*basic.base_type));
-       else
-               throw invalid_argument("SpirVGenerator::get_element_type");
-}
-
 void SpirVGenerator::visit(Block &block)
 {
-       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
-               (*i)->visit(*this);
+       for(const RefPtr<Statement> &s: block.body)
+               s->visit(*this);
 }
 
 void SpirVGenerator::visit(Literal &literal)
@@ -478,6 +498,7 @@ void SpirVGenerator::visit(Literal &literal)
                r_expression_result_id = write_constant(type_id, get_constant_key(type_id, literal.value).int_value, true);
        else
                r_expression_result_id = get_constant_id(type_id, literal.value);
+       r_constant_result = true;
 }
 
 void SpirVGenerator::visit(VariableReference &var)
@@ -488,11 +509,13 @@ void SpirVGenerator::visit(VariableReference &var)
                        throw internal_error("reference to non-constant variable in constant context");
 
                r_expression_result_id = get_id(*var.declaration);
+               r_constant_result = true;
                return;
        }
        else if(!current_function)
                throw internal_error("non-constant context outside a function");
 
+       r_constant_result = false;
        if(composite_access)
        {
                r_composite_base = var.declaration;
@@ -515,6 +538,7 @@ void SpirVGenerator::visit(InterfaceBlockReference &iface)
 
        r_composite_base = iface.declaration;
        r_expression_result_id = 0;
+       r_constant_result = false;
 }
 
 void SpirVGenerator::generate_composite_access(TypeDeclaration &result_type)
@@ -528,13 +552,13 @@ void SpirVGenerator::generate_composite_access(TypeDeclaration &result_type)
                        throw internal_error("composite access through pointer in constant context");
 
                Id int32_type_id = get_standard_type_id(BasicTypeDeclaration::INT, 1);
-               for(vector<unsigned>::iterator i=r_composite_chain.begin(); i!=r_composite_chain.end(); ++i)
-                       *i = (*i<0x400000 ? get_constant_id(int32_type_id, static_cast<int>(*i)) : *i&0x3FFFFF);
+               for(unsigned &i: r_composite_chain)
+                       i = (i<0x400000 ? get_constant_id(int32_type_id, static_cast<int>(i)) : i&0x3FFFFF);
 
                /* Find the storage class of the base and obtain appropriate pointer type
                for the result. */
                const Declaration &base_decl = get_item(declared_ids, r_composite_base);
-               map<TypeKey, Id>::const_iterator i = pointer_type_ids.begin();
+               auto i = pointer_type_ids.begin();
                for(; (i!=pointer_type_ids.end() && i->second!=base_decl.type_id); ++i) ;
                if(i==pointer_type_ids.end())
                        throw internal_error("could not find storage class");
@@ -546,20 +570,21 @@ void SpirVGenerator::generate_composite_access(TypeDeclaration &result_type)
                throw internal_error("assignment to temporary composite");
        else
        {
-               for(vector<unsigned>::iterator i=r_composite_chain.begin(); i!=r_composite_chain.end(); ++i)
-                       for(map<ConstantKey, Id>::iterator j=constant_ids.begin(); (*i>=0x400000 && j!=constant_ids.end()); ++j)
-                               if(j->second==(*i&0x3FFFFF))
-                                       *i = j->first.int_value;
+               for(unsigned i: r_composite_chain)
+                       for(auto j=constant_ids.begin(); (i>=0x400000 && j!=constant_ids.end()); ++j)
+                               if(j->second==(i&0x3FFFFF))
+                                       i = j->first.int_value;
 
                opcode = OP_COMPOSITE_EXTRACT;
        }
 
        Id access_id = begin_expression(opcode, access_type_id, 1+r_composite_chain.size());
        writer.write(r_composite_base_id);
-       for(vector<unsigned>::const_iterator i=r_composite_chain.begin(); i!=r_composite_chain.end(); ++i)
-               writer.write(*i);
+       for(unsigned i: r_composite_chain)
+               writer.write(i);
        end_expression(opcode);
 
+       r_constant_result = false;
        if(r_composite_base)
        {
                if(assignment_source_id)
@@ -653,6 +678,7 @@ void SpirVGenerator::visit(Swizzle &swizzle)
                        writer.write(swizzle.components[i]);
                end_expression(OP_VECTOR_SHUFFLE);
        }
+       r_constant_result = false;
 }
 
 void SpirVGenerator::visit(UnaryExpression &unary)
@@ -665,7 +691,7 @@ void SpirVGenerator::visit(UnaryExpression &unary)
                return;
 
        BasicTypeDeclaration &basic = dynamic_cast<BasicTypeDeclaration &>(*unary.expression->type);
-       BasicTypeDeclaration &elem = get_element_type(basic);
+       BasicTypeDeclaration &elem = *get_element_type(basic);
 
        if(constant_expression && elem.kind!=BasicTypeDeclaration::BOOL && elem.kind!=BasicTypeDeclaration::INT)
                /* SPIR-V allows constant operations on floating-point values only for
@@ -675,6 +701,7 @@ void SpirVGenerator::visit(UnaryExpression &unary)
        Id result_type_id = get_id(*unary.type);
        Opcode opcode = OP_NOP;
 
+       r_constant_result = false;
        if(oper=='!')
                opcode = OP_LOGICAL_NOT;
        else if(oper=='~')
@@ -747,7 +774,7 @@ void SpirVGenerator::visit(BinaryExpression &binary)
        BasicTypeDeclaration &basic_left = dynamic_cast<BasicTypeDeclaration &>(*binary.left->type);
        BasicTypeDeclaration &basic_right = dynamic_cast<BasicTypeDeclaration &>(*binary.right->type);
        // Expression resolver ensures that element types are the same
-       BasicTypeDeclaration &elem = get_element_type(basic_left);
+       BasicTypeDeclaration &elem = *get_element_type(basic_left);
 
        if(constant_expression && elem.kind!=BasicTypeDeclaration::BOOL && elem.kind!=BasicTypeDeclaration::INT)
                /* SPIR-V allows constant operations on floating-point values only for
@@ -763,12 +790,20 @@ void SpirVGenerator::visit(BinaryExpression &binary)
        Opcode opcode = OP_NOP;
        bool swap_operands = false;
 
+       r_constant_result = false;
+
        char oper2 = binary.oper->token[1];
        if((oper=='<' || oper=='>') && oper2!=oper)
        {
                if(basic_left.kind==BasicTypeDeclaration::INT)
-                       opcode = (oper=='<' ? (oper2=='=' ? OP_S_LESS_THAN_EQUAL : OP_S_LESS_THAN) :
-                               (oper2=='=' ? OP_S_GREATER_THAN_EQUAL : OP_S_GREATER_THAN));
+               {
+                       if(basic_left.sign)
+                               opcode = (oper=='<' ? (oper2=='=' ? OP_S_LESS_THAN_EQUAL : OP_S_LESS_THAN) :
+                                       (oper2=='=' ? OP_S_GREATER_THAN_EQUAL : OP_S_GREATER_THAN));
+                       else
+                               opcode = (oper=='<' ? (oper2=='=' ? OP_U_LESS_THAN_EQUAL : OP_U_LESS_THAN) :
+                                       (oper2=='=' ? OP_U_GREATER_THAN_EQUAL : OP_U_GREATER_THAN));
+               }
                else if(basic_left.kind==BasicTypeDeclaration::FLOAT)
                        opcode = (oper=='<' ? (oper2=='=' ? OP_F_ORD_LESS_THAN_EQUAL : OP_F_ORD_LESS_THAN) :
                                (oper2=='=' ? OP_F_ORD_GREATER_THAN_EQUAL : OP_F_ORD_GREATER_THAN));
@@ -833,12 +868,17 @@ void SpirVGenerator::visit(BinaryExpression &binary)
        else if(oper=='>' && oper2==oper && elem.kind==BasicTypeDeclaration::INT)
                opcode = OP_SHIFT_RIGHT_ARITHMETIC;
        else if(oper=='%' && elem.kind==BasicTypeDeclaration::INT)
-               opcode = OP_S_MOD;
+               opcode = (elem.sign ? OP_S_MOD : OP_U_MOD);
        else if(oper=='+' || oper=='-' || oper=='*' || oper=='/')
        {
                Opcode elem_op = OP_NOP;
                if(elem.kind==BasicTypeDeclaration::INT)
-                       elem_op = (oper=='+' ? OP_I_ADD : oper=='-' ? OP_I_SUB : oper=='*' ? OP_I_MUL : OP_S_DIV);
+               {
+                       if(oper=='/')
+                               elem_op = (elem.sign ? OP_S_DIV : OP_U_DIV);
+                       else
+                               elem_op = (oper=='+' ? OP_I_ADD : oper=='-' ? OP_I_SUB : OP_I_MUL);
+               }
                else if(elem.kind==BasicTypeDeclaration::FLOAT)
                        elem_op = (oper=='+' ? OP_F_ADD : oper=='-' ? OP_F_SUB : oper=='*' ? OP_F_MUL : OP_F_DIV);
 
@@ -946,6 +986,7 @@ void SpirVGenerator::visit(Assignment &assign)
 
        SetForScope<Id> set_assign(assignment_source_id, r_expression_result_id);
        assign.left->visit(*this);
+       r_constant_result = false;
 }
 
 void SpirVGenerator::visit(TernaryExpression &ternary)
@@ -993,13 +1034,13 @@ void SpirVGenerator::visit(TernaryExpression &ternary)
        writer.write(false_result_id);
        writer.write(false_label_id);
        end_expression(OP_PHI);
+
+       r_constant_result = false;
 }
 
 void SpirVGenerator::visit(FunctionCall &call)
 {
-       if(constant_expression)
-               throw internal_error("function call in constant expression");
-       else if(assignment_source_id)
+       if(assignment_source_id)
                throw internal_error("assignment to function call");
        else if(composite_access)
                return visit_isolated(call);
@@ -1008,27 +1049,33 @@ void SpirVGenerator::visit(FunctionCall &call)
 
        vector<Id> argument_ids;
        argument_ids.reserve(call.arguments.size());
-       for(NodeArray<Expression>::const_iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
+       bool all_args_const = true;
+       for(const RefPtr<Expression> &a: call.arguments)
        {
-               (*i)->visit(*this);
+               a->visit(*this);
                argument_ids.push_back(r_expression_result_id);
+               all_args_const &= r_constant_result;
        }
 
+       if(constant_expression && (!call.constructor || !all_args_const))
+               throw internal_error("function call in constant expression");
+
        Id result_type_id = get_id(*call.type);
+       r_constant_result = false;
 
        if(call.constructor)
-               visit_constructor(call, argument_ids);
+               visit_constructor(call, argument_ids, all_args_const);
        else if(call.declaration->source==BUILTIN_SOURCE)
        {
                string arg_types;
-               for(NodeArray<Expression>::const_iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
-                       if(BasicTypeDeclaration *basic_arg = dynamic_cast<BasicTypeDeclaration *>((*i)->type))
+               for(const RefPtr<Expression> &a: call.arguments)
+                       if(BasicTypeDeclaration *basic_arg = dynamic_cast<BasicTypeDeclaration *>(a->type))
                        {
-                               BasicTypeDeclaration &elem_arg = get_element_type(*basic_arg);
+                               BasicTypeDeclaration &elem_arg = *get_element_type(*basic_arg);
                                switch(elem_arg.kind)
                                {
                                case BasicTypeDeclaration::BOOL: arg_types += 'b'; break;
-                               case BasicTypeDeclaration::INT: arg_types += 'i'; break;
+                               case BasicTypeDeclaration::INT: arg_types += (elem_arg.sign ? 'i' : 'u'); break;
                                case BasicTypeDeclaration::FLOAT: arg_types += 'f'; break;
                                default: arg_types += '?';
                                }
@@ -1039,6 +1086,9 @@ void SpirVGenerator::visit(FunctionCall &call)
                        if(builtin_info->function==call.name && (!builtin_info->arg_types[0] || builtin_info->arg_types==arg_types))
                                break;
 
+               if(builtin_info->capability)
+                       use_capability(static_cast<Capability>(builtin_info->capability));
+
                if(builtin_info->opcode)
                {
                        Opcode opcode;
@@ -1075,19 +1125,19 @@ void SpirVGenerator::visit(FunctionCall &call)
        {
                r_expression_result_id = begin_expression(OP_FUNCTION_CALL, result_type_id, 1+call.arguments.size());
                writer.write(get_id(*call.declaration->definition));
-               for(vector<Id>::const_iterator i=argument_ids.begin(); i!=argument_ids.end(); ++i)
-                       writer.write(*i);
+               for(Id i: argument_ids)
+                       writer.write(i);
                end_expression(OP_FUNCTION_CALL);
 
                // Any global variables the called function uses might have changed value
                set<Node *> dependencies = DependencyCollector().apply(*call.declaration->definition);
-               for(set<Node *>::const_iterator i=dependencies.begin(); i!=dependencies.end(); ++i)
-                       if(const VariableDeclaration *var = dynamic_cast<const VariableDeclaration *>(*i))
+               for(Node *n: dependencies)
+                       if(const VariableDeclaration *var = dynamic_cast<const VariableDeclaration *>(n))
                                variable_load_ids.erase(var);
        }
 }
 
-void SpirVGenerator::visit_constructor(FunctionCall &call, const vector<Id> &argument_ids)
+void SpirVGenerator::visit_constructor(FunctionCall &call, const vector<Id> &argument_ids, bool all_args_const)
 {
        Id result_type_id = get_id(*call.type);
 
@@ -1101,9 +1151,11 @@ void SpirVGenerator::visit_constructor(FunctionCall &call, const vector<Id> &arg
                return;
        }
 
-       BasicTypeDeclaration &elem = get_element_type(*basic);
+       SetFlag set_const(constant_expression, constant_expression || all_args_const);
+
+       BasicTypeDeclaration &elem = *get_element_type(*basic);
        BasicTypeDeclaration &basic_arg0 = dynamic_cast<BasicTypeDeclaration &>(*call.arguments[0]->type);
-       BasicTypeDeclaration &elem_arg0 = get_element_type(basic_arg0);
+       BasicTypeDeclaration &elem_arg0 = *get_element_type(basic_arg0);
 
        if(basic->kind==BasicTypeDeclaration::MATRIX)
        {
@@ -1118,7 +1170,7 @@ void SpirVGenerator::visit_constructor(FunctionCall &call, const vector<Id> &arg
                        Id zero_id = get_constant_id(get_id(elem), 0.0f);
                        for(unsigned i=0; i<n_columns; ++i)
                        {
-                               column_ids[i] = begin_expression(OP_COMPOSITE_CONSTRUCT, col_type_id, n_rows);;
+                               column_ids[i] = begin_expression(OP_COMPOSITE_CONSTRUCT, col_type_id, n_rows);
                                for(unsigned j=0; j<n_rows; ++j)
                                        writer.write(j==i ? argument_ids[0] : zero_id);
                                end_expression(OP_COMPOSITE_CONSTRUCT);
@@ -1146,6 +1198,9 @@ void SpirVGenerator::visit_constructor(FunctionCall &call, const vector<Id> &arg
        }
        else if(elem.kind==BasicTypeDeclaration::BOOL)
        {
+               if(constant_expression)
+                       throw internal_error("unconverted constant");
+
                // Conversion to boolean is implemented as comparing against zero.
                Id number_type_id = get_id(elem_arg0);
                Id zero_id = (elem_arg0.kind==BasicTypeDeclaration::FLOAT ?
@@ -1158,6 +1213,9 @@ void SpirVGenerator::visit_constructor(FunctionCall &call, const vector<Id> &arg
        }
        else if(elem_arg0.kind==BasicTypeDeclaration::BOOL)
        {
+               if(constant_expression)
+                       throw internal_error("unconverted constant");
+
                /* Conversion from boolean is implemented as selecting from zero
                or one. */
                Id number_type_id = get_id(elem);
@@ -1179,12 +1237,17 @@ void SpirVGenerator::visit_constructor(FunctionCall &call, const vector<Id> &arg
        }
        else
        {
+               if(constant_expression)
+                       throw internal_error("unconverted constant");
+
                // Scalar or vector conversion between types of equal size.
                Opcode opcode;
                if(elem.kind==BasicTypeDeclaration::INT && elem_arg0.kind==BasicTypeDeclaration::FLOAT)
-                       opcode = OP_CONVERT_F_TO_S;
+                       opcode = (elem.sign ? OP_CONVERT_F_TO_S : OP_CONVERT_F_TO_U);
                else if(elem.kind==BasicTypeDeclaration::FLOAT && elem_arg0.kind==BasicTypeDeclaration::INT)
-                       opcode = OP_CONVERT_S_TO_F;
+                       opcode = (elem_arg0.sign ? OP_CONVERT_S_TO_F : OP_CONVERT_U_TO_F);
+               else if(elem.kind==BasicTypeDeclaration::INT && elem_arg0.kind==BasicTypeDeclaration::INT)
+                       opcode = OP_BITCAST;
                else
                        throw internal_error("invalid conversion");
 
@@ -1211,6 +1274,40 @@ void SpirVGenerator::visit_builtin_matrix_comp_mult(FunctionCall &call, const ve
        r_expression_result_id = write_construct(get_id(*call.type), column_ids, n_columns);
 }
 
+void SpirVGenerator::visit_builtin_texture_query(FunctionCall &call, const vector<Id> &argument_ids)
+{
+       if(argument_ids.size()<1)
+               throw internal_error("invalid texture query call");
+
+       Opcode opcode;
+       if(call.name=="textureSize")
+               opcode = OP_IMAGE_QUERY_SIZE_LOD;
+       else if(call.name=="textureQueryLod")
+               opcode = OP_IMAGE_QUERY_LOD;
+       else if(call.name=="textureQueryLevels")
+               opcode = OP_IMAGE_QUERY_LEVELS;
+       else
+               throw internal_error("invalid texture query call");
+
+       ImageTypeDeclaration &image_arg0 = dynamic_cast<ImageTypeDeclaration &>(*call.arguments[0]->type);
+
+       Id image_id;
+       if(image_arg0.sampled)
+       {
+               Id image_type_id = get_item(image_type_ids, get_id(image_arg0));
+               image_id = write_expression(OP_IMAGE, image_type_id, argument_ids[0]);
+       }
+       else
+               image_id = argument_ids[0];
+
+       Id result_type_id = get_id(*call.type);
+       r_expression_result_id = begin_expression(opcode, result_type_id, argument_ids.size());
+       writer.write(image_id);
+       for(unsigned i=1; i<argument_ids.size(); ++i)
+               writer.write(argument_ids[i]);
+       end_expression(opcode);
+}
+
 void SpirVGenerator::visit_builtin_texture(FunctionCall &call, const vector<Id> &argument_ids)
 {
        if(argument_ids.size()<2)
@@ -1291,14 +1388,12 @@ void SpirVGenerator::visit_builtin_interpolate(FunctionCall &call, const vector<
        else
                throw internal_error("invalid interpolate call");
 
-       use_capability(CAP_INTERPOLATION_FUNCTION);
-
        Id ext_id = import_extension("GLSL.std.450");
        r_expression_result_id = begin_expression(OP_EXT_INST, get_id(*call.type));
        writer.write(ext_id);
        writer.write(opcode);
        writer.write(get_id(*var->declaration));
-       for(vector<Id>::const_iterator i=argument_ids.begin(); ++i!=argument_ids.end(); )
+       for(auto i=argument_ids.begin(); ++i!=argument_ids.end(); )
                writer.write(*i);
        end_expression(OP_EXT_INST);
 }
@@ -1315,11 +1410,11 @@ void SpirVGenerator::visit(InterfaceLayout &layout)
 
 bool SpirVGenerator::check_duplicate_type(TypeDeclaration &type)
 {
-       for(map<Node *, Declaration>::const_iterator i=declared_ids.begin(); i!=declared_ids.end(); ++i)
-               if(TypeDeclaration *type2 = dynamic_cast<TypeDeclaration *>(i->first))
+       for(const auto &kvp: declared_ids)
+               if(TypeDeclaration *type2 = dynamic_cast<TypeDeclaration *>(kvp.first))
                        if(TypeComparer().apply(type, *type2))
                        {
-                               insert_unique(declared_ids, &type, i->second);
+                               insert_unique(declared_ids, &type, kvp.second);
                                return true;
                        }
 
@@ -1335,7 +1430,7 @@ bool SpirVGenerator::check_standard_type(BasicTypeDeclaration &basic)
        if((elem->kind==BasicTypeDeclaration::INT || elem->kind==BasicTypeDeclaration::FLOAT) && elem->size!=32)
                return false;
 
-       Id standard_id = get_standard_type_id(elem->kind, (basic.kind==BasicTypeDeclaration::VECTOR ? basic.size : 1));
+       Id standard_id = get_standard_type_id(elem->kind, (basic.kind==BasicTypeDeclaration::VECTOR ? basic.size : 1), elem->sign);
        insert_unique(declared_ids, &basic, Declaration(standard_id, 0));
        writer.write_op_name(standard_id, basic.name);
 
@@ -1358,7 +1453,7 @@ void SpirVGenerator::visit(BasicTypeDeclaration &basic)
        switch(basic.kind)
        {
        case BasicTypeDeclaration::INT:
-               writer.write_op(content.globals, OP_TYPE_INT, type_id, basic.size, 1);
+               writer.write_op(content.globals, OP_TYPE_INT, type_id, basic.size, basic.sign);
                break;
        case BasicTypeDeclaration::FLOAT:
                writer.write_op(content.globals, OP_TYPE_FLOAT, type_id, basic.size);
@@ -1397,6 +1492,7 @@ void SpirVGenerator::visit(ImageTypeDeclaration &image)
        {
                writer.write_op_name(type_id, image.name);
                writer.write_op(content.globals, OP_TYPE_SAMPLED_IMAGE, type_id, image_id);
+               insert_unique(image_type_ids, type_id, image_id);
        }
 
        if(image.dimensions==ImageTypeDeclaration::ONE)
@@ -1419,9 +1515,9 @@ void SpirVGenerator::visit(StructDeclaration &strct)
        bool builtin = (strct.interface_block && !strct.interface_block->block_name.compare(0, 3, "gl_"));
        vector<Id> member_type_ids;
        member_type_ids.reserve(strct.members.body.size());
-       for(NodeList<Statement>::const_iterator i=strct.members.body.begin(); i!=strct.members.body.end(); ++i)
+       for(const RefPtr<Statement> &s: strct.members.body)
        {
-               const VariableDeclaration *var = dynamic_cast<const VariableDeclaration *>(i->get());
+               const VariableDeclaration *var = dynamic_cast<const VariableDeclaration *>(s.get());
                if(!var)
                        continue;
 
@@ -1439,14 +1535,13 @@ void SpirVGenerator::visit(StructDeclaration &strct)
                {
                        if(var->layout)
                        {
-                               const vector<Layout::Qualifier> &qualifiers = var->layout->qualifiers;
-                               for(vector<Layout::Qualifier>::const_iterator j=qualifiers.begin(); j!=qualifiers.end(); ++j)
+                               for(const Layout::Qualifier &q: var->layout->qualifiers)
                                {
-                                       if(j->name=="offset")
-                                               writer.write_op_member_decorate(type_id, index, DECO_OFFSET, j->value);
-                                       else if(j->name=="column_major")
+                                       if(q.name=="offset")
+                                               writer.write_op_member_decorate(type_id, index, DECO_OFFSET, q.value);
+                                       else if(q.name=="column_major")
                                                writer.write_op_member_decorate(type_id, index, DECO_COL_MAJOR);
-                                       else if(j->name=="row_major")
+                                       else if(q.name=="row_major")
                                                writer.write_op_member_decorate(type_id, index, DECO_ROW_MAJOR);
                                }
                        }
@@ -1464,8 +1559,8 @@ void SpirVGenerator::visit(StructDeclaration &strct)
 
        writer.begin_op(content.globals, OP_TYPE_STRUCT);
        writer.write(type_id);
-       for(vector<Id>::const_iterator i=member_type_ids.begin(); i!=member_type_ids.end(); ++i)
-               writer.write(*i);
+       for(Id i: member_type_ids)
+               writer.write(i);
        writer.end_op(OP_TYPE_STRUCT);
 }
 
@@ -1476,9 +1571,9 @@ void SpirVGenerator::visit(VariableDeclaration &var)
        int spec_id = -1;
        if(layout_ql)
        {
-               for(vector<Layout::Qualifier>::const_iterator i=layout_ql->begin(); (spec_id<0 && i!=layout_ql->end()); ++i)
-                       if(i->name=="constant_id")
-                               spec_id = i->value;
+               auto i = find_member(*layout_ql, string("constant_id"), &Layout::Qualifier::name);
+               if(i!=layout_ql->end())
+                       spec_id = i->value;
        }
 
        Id type_id = get_variable_type_id(var);
@@ -1525,6 +1620,7 @@ void SpirVGenerator::visit(VariableDeclaration &var)
                {
                        SetFlag set_const(constant_expression, !current_function);
                        r_expression_result_id = 0;
+                       r_constant_result = false;
                        var.init_expression->visit(*this);
                        init_id = r_expression_result_id;
                }
@@ -1540,19 +1636,22 @@ void SpirVGenerator::visit(VariableDeclaration &var)
 
                if(layout_ql)
                {
-                       for(vector<Layout::Qualifier>::const_iterator i=layout_ql->begin(); i!=layout_ql->end(); ++i)
+                       for(const Layout::Qualifier &q: *layout_ql)
                        {
-                               if(i->name=="location")
-                                       writer.write_op_decorate(var_id, DECO_LOCATION, i->value);
-                               else if(i->name=="set")
-                                       writer.write_op_decorate(var_id, DECO_DESCRIPTOR_SET, i->value);
-                               else if(i->name=="binding")
-                                       writer.write_op_decorate(var_id, DECO_BINDING, i->value);
+                               if(q.name=="location")
+                                       writer.write_op_decorate(var_id, DECO_LOCATION, q.value);
+                               else if(q.name=="set")
+                                       writer.write_op_decorate(var_id, DECO_DESCRIPTOR_SET, q.value);
+                               else if(q.name=="binding")
+                                       writer.write_op_decorate(var_id, DECO_BINDING, q.value);
                        }
                }
 
                if(init_id && current_function)
+               {
                        writer.write_op(content.function_body, OP_STORE, var_id, init_id);
+                       variable_load_ids[&var] = init_id;
+               }
        }
 
        writer.write_op_name(var_id, var.name);
@@ -1588,10 +1687,9 @@ void SpirVGenerator::visit(InterfaceBlock &iface)
 
        if(iface.layout)
        {
-               const vector<Layout::Qualifier> &qualifiers = iface.layout->qualifiers;
-               for(vector<Layout::Qualifier>::const_iterator i=qualifiers.begin(); i!=qualifiers.end(); ++i)
-                       if(i->name=="binding")
-                               writer.write_op_decorate(block_id, DECO_BINDING, i->value);
+               auto i = find_member(iface.layout->qualifiers, string("binding"), &Layout::Qualifier::name);
+               if(i!=iface.layout->qualifiers.end())
+                       writer.write_op_decorate(block_id, DECO_BINDING, i->value);
        }
 }
 
@@ -1609,15 +1707,15 @@ void SpirVGenerator::visit_entry_point(FunctionDeclaration &func, Id func_id)
        writer.write_string(func.name);
 
        set<Node *> dependencies = DependencyCollector().apply(func);
-       for(set<Node *>::const_iterator i=dependencies.begin(); i!=dependencies.end(); ++i)
+       for(Node *n: dependencies)
        {
-               if(const VariableDeclaration *var = dynamic_cast<const VariableDeclaration *>(*i))
+               if(const VariableDeclaration *var = dynamic_cast<const VariableDeclaration *>(n))
                {
                        if(!var->interface.empty())
-                               writer.write(get_id(**i));
+                               writer.write(get_id(*n));
                }
-               else if(dynamic_cast<InterfaceBlock *>(*i))
-                       writer.write(get_id(**i));
+               else if(dynamic_cast<InterfaceBlock *>(n))
+                       writer.write(get_id(*n));
        }
 
        writer.end_op(OP_ENTRY_POINT);
@@ -1627,42 +1725,47 @@ void SpirVGenerator::visit_entry_point(FunctionDeclaration &func, Id func_id)
        else if(stage->type==Stage::GEOMETRY)
                use_capability(CAP_GEOMETRY);
 
-       for(vector<const InterfaceLayout *>::const_iterator i=interface_layouts.begin(); i!=interface_layouts.end(); ++i)
+       for(const InterfaceLayout *i: interface_layouts)
        {
-               const vector<Layout::Qualifier> &qualifiers = (*i)->layout.qualifiers;
-               for(vector<Layout::Qualifier>::const_iterator j=qualifiers.begin(); j!=qualifiers.end(); ++j)
+               for(const Layout::Qualifier &q: i->layout.qualifiers)
                {
-                       if(j->name=="point")
+                       if(q.name=="point")
                                writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id,
-                                       ((*i)->interface=="in" ? EXEC_INPUT_POINTS : EXEC_OUTPUT_POINTS));
-                       else if(j->name=="lines")
+                                       (i->interface=="in" ? EXEC_INPUT_POINTS : EXEC_OUTPUT_POINTS));
+                       else if(q.name=="lines")
                                writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_INPUT_LINES);
-                       else if(j->name=="lines_adjacency")
+                       else if(q.name=="lines_adjacency")
                                writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_INPUT_LINES_ADJACENCY);
-                       else if(j->name=="triangles")
+                       else if(q.name=="triangles")
                                writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_TRIANGLES);
-                       else if(j->name=="triangles_adjacency")
+                       else if(q.name=="triangles_adjacency")
                                writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_INPUT_TRIANGLES_ADJACENCY);
-                       else if(j->name=="line_strip")
+                       else if(q.name=="line_strip")
                                writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_OUTPUT_LINE_STRIP);
-                       else if(j->name=="triangle_strip")
+                       else if(q.name=="triangle_strip")
                                writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_OUTPUT_TRIANGLE_STRIP);
-                       else if(j->name=="max_vertices")
-                               writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_OUTPUT_VERTICES, j->value);
+                       else if(q.name=="max_vertices")
+                               writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_OUTPUT_VERTICES, q.value);
                }
        }
 }
 
 void SpirVGenerator::visit(FunctionDeclaration &func)
 {
-       if(func.source==BUILTIN_SOURCE || func.definition!=&func)
+       if(func.source==BUILTIN_SOURCE)
+               return;
+       else if(func.definition!=&func)
+       {
+               if(func.definition)
+                       allocate_forward_id(*func.definition);
                return;
+       }
 
        Id return_type_id = get_id(*func.return_type_declaration);
        vector<unsigned> param_type_ids;
        param_type_ids.reserve(func.parameters.size());
-       for(NodeArray<VariableDeclaration>::const_iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
-               param_type_ids.push_back(get_variable_type_id(**i));
+       for(const RefPtr<VariableDeclaration> &p: func.parameters)
+               param_type_ids.push_back(get_variable_type_id(*p));
 
        string sig_with_return = func.return_type+func.signature;
        Id &type_id = function_type_ids[sig_with_return];
@@ -1672,8 +1775,8 @@ void SpirVGenerator::visit(FunctionDeclaration &func)
                writer.begin_op(content.globals, OP_TYPE_FUNCTION);
                writer.write(type_id);
                writer.write(return_type_id);
-               for(vector<unsigned>::const_iterator i=param_type_ids.begin(); i!=param_type_ids.end(); ++i)
-                       writer.write(*i);
+               for(unsigned i: param_type_ids)
+                       writer.write(i);
                writer.end_op(OP_TYPE_FUNCTION);
 
                writer.write_op_name(type_id, sig_with_return);
@@ -1755,6 +1858,8 @@ void SpirVGenerator::visit(Iteration &iter)
        if(iter.init_statement)
                iter.init_statement->visit(*this);
 
+       variable_load_ids.clear();
+
        Id header_id = next_id++;
        Id continue_id = next_id++;
        Id merge_block_id = next_id++;
@@ -1812,6 +1917,19 @@ void SpirVGenerator::visit(Jump &jump)
 }
 
 
+SpirVGenerator::TypeKey::TypeKey(BasicTypeDeclaration::Kind kind, bool sign):
+       type_id(0)
+{
+       switch(kind)
+       {
+       case BasicTypeDeclaration::VOID: detail = 'v'; break;
+       case BasicTypeDeclaration::BOOL: detail = 'b'; break;
+       case BasicTypeDeclaration::INT: detail = (sign ? 'i' : 'u'); break;
+       case BasicTypeDeclaration::FLOAT: detail = 'f'; break;
+       default: throw invalid_argument("TypeKey::TypeKey");
+       }
+}
+
 bool SpirVGenerator::TypeKey::operator<(const TypeKey &other) const
 {
        if(type_id!=other.type_id)