glslang SPIR-V Usage
SkillDev toolsglslang SPIR-V Builder API for types, instructions, control flow, and decorations.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the glslang SPIR-V Usage skill
What this skill tells your AI
The instructions your AI receives, as published by luisagroup/luisacompute in .agents/skills/glslang/SKILL.md and read by ahel’s review.
Located in src/ext/glslang/SPIRV. Headers:
#include "SPIRV/SpvBuilder.h"
#include "SPIRV/spvIR.h"
#include "SPIRV/GlslangToSpv.h"
#include "SPIRV/disassemble.h"
Code snippets follow glslang's own conventions (e.g.
camelCasebuilder methods). LuisaCompute project style rules apply to project code, whilesrc/ext/glslangis third-party code.
SpvBuilder Lifecycle
spv::Builder owns one SPIR-V module. Thread-safe internal IR.
spv::SpvBuildLogger logger;
spv::Builder builder(spv::Spv_1_5, 0, &logger);
builder.setSource(spv::SourceLanguage::GLSL, 450);
builder.setMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450);
builder.addCapability(spv::Capability::Shader);
// ... build ...
std::vector<unsigned int> spirv;
builder.dump(spirv);
Module Setup
builder.setSource(spv::SourceLanguage::GLSL, 450);
builder.setEmitSpirvDebugInfo(); // required before setting debug locations
builder.setDebugMainSourceFile("shader.frag");
builder.setDebugSourceLocation(10, "shader.frag");
builder.addCapability(spv::Capability::Shader);
builder.addExtension("SPV_KHR_ray_tracing");
builder.setMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450);
spv::Id glsl450 = builder.import("GLSL.std.450");
Types (canonicalized)
spv::Id voidTy = builder.makeVoidType();
spv::Id boolTy = builder.makeBoolType();
spv::Id int32Ty = builder.makeIntType(32);
spv::Id uint32Ty = builder.makeUintType(32);
spv::Id uint64Ty = builder.makeUintType(64);
spv::Id floatTy = builder.makeFloatType(32);
spv::Id doubleTy = builder.makeFloatType(64);
spv::Id halfTy = builder.makeFloatType(16);
spv::Id bfloat16 = builder.makeBFloat16Type();
spv::Id float8e5 = builder.makeFloatE5M2Type();
spv::Id float8e4 = builder.makeFloatE4M3Type();
spv::Id vec4Ty = builder.makeVectorType(floatTy, 4);
spv::Id mat4x4Ty = builder.makeMatrixType(floatTy, 4, 4);
spv::Id arrTy = builder.makeArrayType(floatTy, builder.makeUintConstant(16), 0);
spv::Id runArrTy = builder.makeRuntimeArray(floatTy);
std::vector<spv::Id> members = {floatTy, int32Ty};
// Second argument is member debug info; use {} when no per-member debug data is needed.
spv::Id structTy = builder.makeStructType(members, {}, "MyStruct", false);
spv::Id ptrTy = builder.makePointer(spv::StorageClass::Function, floatTy);
spv::Id fwdPtrTy = builder.makeForwardPointer(spv::StorageClass::PhysicalStorageBuffer);
// Resolve a forward pointer to its pointee type once the pointee is known.
spv::Id resolvedPtrTy = builder.makePointerFromForwardPointer(spv::StorageClass::PhysicalStorageBuffer, fwdPtrTy, floatTy);
spv::Id untypedPtr= builder.makeUntypedPointer(spv::StorageClass::StorageBuffer);
spv::Id fnTy = builder.makeFunctionType(voidTy, {floatTy, int32Ty});
spv::Id imgTy = builder.makeImageType(floatTy, spv::Dim::Dim2D, false, false, false, 1, spv::ImageFormat::Rgba32f, "texture2D");
spv::Id sampledImgTy= builder.makeSampledImageType(imgTy, "sampler2D");
spv::Id samplerTy = builder.makeSamplerType("sampler");
spv::Id asTy = builder.makeAccelerationStructureType();
spv::Id rqTy = builder.makeRayQueryType();
spv::Id hoTy = builder.makeHitObjectEXTType();
spv::Id coopMatTy = builder.makeCooperativeMatrixTypeKHR(floatTy, scopeId, rowsId, colsId, useId);
spv::Id coopVecTy = builder.makeCooperativeVectorTypeNV(floatTy, componentsId);
spv::Id tensorTy = builder.makeTensorTypeARM(floatTy, rankId);
// Generic
std::vector<spv::IdImmediate> ops = {{true, someId}};
spv::Id genericTy = builder.makeGenericType(spv::Op::OpType..., ops);
Type Queries
spv::Id typeId = builder.getTypeId(resultId);
spv::Op opCode = builder.getOpCode(id);
spv::Op cls = builder.getTypeClass(typeId);
bool isPtr = builder.isPointer(id);
bool isScalar = builder.isScalar(id);
bool isVec = builder.isVector(id);
bool isMat = builder.isMatrix(id);
bool isArray = builder.isArrayType(typeId);
bool isStruct = builder.isStructType(typeId);
bool isImage = builder.isImageType(typeId);
bool isSampler = builder.isSamplerType(typeId);
int width = builder.getScalarTypeWidth(typeId);
spv::Id scalar = builder.getScalarTypeId(typeId);
spv::Id contained = builder.getContainedTypeId(typeId); // single
spv::Id contained = builder.getContainedTypeId(typeId, n); // nth
unsigned cols = builder.getNumColumns(id);
unsigned rows = builder.getNumRows(id);
unsigned comps= builder.getNumComponents(id);
Constants (deduplicated; spec constants not)
spv::Id t = builder.makeBoolConstant(true), f = builder.makeBoolConstant(false);
spv::Id i32 = builder.makeIntConstant(5), u32 = builder.makeUintConstant(7);
spv::Id i64 = builder.makeInt64Constant(9), u64 = builder.makeUint64Constant(11);
spv::Id i8 = builder.makeInt8Constant(1), u8 = builder.makeUint8Constant(2);
spv::Id i16 = builder.makeInt16Constant(3), u16 = builder.makeUint16Constant(4);
spv::Id f32 = builder.makeFloatConstant(1.0f), f64 = builder.makeDoubleConstant(2.0);
spv::Id f16 = builder.makeFloat16Constant(3.0f), bf16 = builder.makeBFloat16Constant(4.0f);
spv::Id fp = builder.makeFpConstant(floatTy, 1.5, false);
spv::Id null= builder.makeNullConstant(structTy);
// Composite
spv::Id vec4 = builder.makeCompositeConstant(vec4Ty, {f32, f32, f32, f32});
// Spec constants
spv::Id specI32 = builder.makeIntConstant(builder.makeIntType(32), 10, true);
spv::Id specVec = builder.makeCompositeConstant(vec4Ty, {f32, f32, f32, f32}, true);
Variables
spv::Id global = builder.createVariable(spv::Decoration::NoPrecision, spv::StorageClass::Private, floatTy, "g", builder.makeFloatConstant(0.0f));
spv::Id local = builder.createVariable(spv::Decoration::NoPrecision, spv::StorageClass::Function, floatTy, "l");
spv::Id untyped= builder.createUntypedVariable(spv::Decoration::NoPrecision, spv::StorageClass::StorageBuffer, "u", dataTypeId, initId);
spv::Id undef = builder.createUndefined(floatTy);
Functions
// Entry point
spv::Function* entry = builder.makeEntryPoint("main");
builder.addEntryPoint(spv::ExecutionModel::Fragment, entry, "main");
builder.addExecutionMode(entry, spv::ExecutionMode::OriginUpperLeft);
// Regular function
spv::Block* entryBlock = nullptr;
spv::Function* func = builder.makeFunctionEntry(
spv::Decoration::NoPrecision, floatTy, "myFunc", spv::LinkageType::Max,
{floatTy, int32Ty},
{{spv::Decoration::NoPrecision}, {spv::Decoration::NoPrecision}},
&entryBlock);
builder.enterFunction(func);
builder.setBuildPoint(entryBlock);
spv::Id p0 = func->getParamId(0);
spv::Id p1 = func->getParamId(1);
builder.makeReturn(false, resultId); // or makeReturn(false) for void
builder.leaveFunction();
Control Flow
If-Then-Else
spv::Builder::If ifBuilder(cond, spv::SelectionControlMask::MaskNone, builder);
// then block
ifBuilder.makeBeginElse();
// else block
ifBuilder.makeEndIf();
// merge block
Switch
std::vector<int> caseValues = {0, 1}, valueToSegment = {0, 1};
int defaultSegment = 2, numSegments = 3;
std::vector<Block*> segmentBB;
builder.makeSwitch(selectorId, spv::SelectionControlMask::MaskNone, numSegments, caseValues, valueToSegment, defaultSegment, segmentBB);
builder.nextSwitchSegment(segmentBB, 0); /* ... */ builder.addSwitchBreak(false);
builder.nextSwitchSegment(segmentBB, 1); /* ... */ builder.addSwitchBreak(false);
builder.nextSwitchSegment(segmentBB, 2); /* ... */ builder.addSwitchBreak(false);
builder.endSwitch(segmentBB);
Loops
spv::Builder::LoopBlocks& loop = builder.makeNewLoop();
builder.setBuildPoint(&loop.head);
builder.createLoopMerge(&loop.merge, &loop.continue_target, spv::LoopControlMask::MaskNone, {});
builder.createConditionalBranch(cond, &loop.body, &loop.merge);
builder.setBuildPoint(&loop.body);
// loop body
builder.createLoopContinue();
builder.setBuildPoint(&loop.continue_target);
// loop increment (optional)
builder.createBranch(false, &loop.head);
builder.setBuildPoint(&loop.merge);
builder.closeLoop();
// break: builder.createLoopExit(); continue: builder.createLoopContinue();
Arithmetic & Logic
spv::Id neg = builder.createUnaryOp(spv::Op::OpSNegate, int32Ty, val);
spv::Id notb = builder.createUnaryOp(spv::Op::OpLogicalNot, boolTy, bval);
spv::Id add = builder.createBinOp(spv::Op::OpFAdd, floatTy, a, b);
spv::Id sub = builder.createBinOp(spv::Op::OpISub, int32Ty, a, b);
spv::Id mul = builder.createBinOp(spv::Op::OpIMul, int32Ty, a, b);
spv::Id div = builder.createBinOp(spv::Op::OpFDiv, floatTy, a, b);
spv::Id and_ = builder.createBinOp(spv::Op::OpBitwiseAnd, uint32Ty, a, b);
// ExtInst (ternary)
spv::Id fma = builder.createOp(spv::Op::OpExtInst, floatTy, {glsl450, GLSLstd450Fma, a, b, c});
// Generic n-ary
spv::Id r = builder.createOp(spv::Op::OpVectorTimesMatrix, vec4Ty, {a, b, c});
// Mixed ID/immediates
std::vector<spv::IdImmediate> mixed = {{true, idOp}, {false, (unsigned)spv::MemoryAccessMask::Aligned}};
spv::Id r = builder.createOp(spv::Op::Op..., typeId, mixed);
// SpecConstantOp
spv::Id specAdd = builder.createSpecConstantOp(spv::Op::OpIAdd, int32Ty, {specA, specB}, {});
Memory Instructions
spv::Id loaded = builder.createLoad(ptrId, spv::Decoration::NoPrecision);
builder.createStore(valueId, ptrId);
builder.createStore(valueId, ptrId, spv::MemoryAccessMask::NonUniformPointerEXT, spv::Scope::Device, 4);
// Access chain
std::vector<spv::Id> indexes = {builder.makeUintConstant(0), builder.makeUintConstant(2)};
spv::Id chain = builder.createAccessChain(spv::StorageClass::Function, basePtr, indexes);
// Composite
spv::Id elem = builder.createCompositeExtract(composite, elemType, 2);
spv::Id elem = builder.createCompositeExtract(composite, elemType, std::vector<unsigned>{0, 1});
spv::Id ins = builder.createCompositeInsert(newVal, composite, compositeType, 0);
spv::Id dynEl = builder.createVectorExtractDynamic(vec, elemType, indexId);
spv::Id dynVec= builder.createVectorInsertDynamic(vec, vecType, newElem, indexId);
spv::Id comp = builder.createCompositeConstruct(vec4Ty, {a, b, c, d});
spv::Id vec4 = builder.createConstructor(spv::Decoration::NoPrecision, {scalarId}, vec4Ty);
spv::Id mat = builder.createMatrixConstructor(spv::Decoration::NoPrecision, srcs, mat4x4Ty);
// Swizzle
spv::Id swz = builder.createRvalueSwizzle(spv::Decoration::NoPrecision, vec4Ty, vec, {2, 1, 0, 3});
spv::Id lswz= builder.createLvalueSwizzle(vec4Ty, target, source, {2, 1, 0, 3});
// Scalar promotion (in-place)
builder.promoteScalar(spv::Decoration::NoPrecision, left, right);
spv::Id smeared = builder.smearScalar(spv::Decoration::NoPrecision, scalarId, vec4Ty);
Access Chain Helper
Builder maintains one active access chain for l-value/r-value tracking:
builder.clearAccessChain();
builder.setAccessChainLValue(ptrId); // base is pointer
builder.setAccessChainRValue(valueId); // base is r-value
builder.accessChainPush(indexId, coherentFlags, alignment);
builder.accessChainPushSwizzle(channels, preSwizzleBaseType, coherentFlags, alignment);
builder.accessChainPushComponent(componentId, preSwizzleBaseType, coherentFlags, alignment);
spv::Id result = builder.accessChainLoad(precision, lvalNonUniform, rvalNonUniform, resultType, memAccess, scope, n);
builder.accessChainStore(valueId, spv::Decoration::NonUniform,
spv::MemoryAccessMask::MaskNone, spv::Scope::Max, 0);
spv::Id lval = builder.accessChainGetLValue();
spv::Id inferred = builder.accessChainGetInferredType();
bool canBeLvalue = builder.isSpvLvalue(); // false for multi-component swizzles like .yx
// Save/restore
spv::Builder::AccessChain saved = builder.getAccessChain();
builder.setAccessChain(saved);
Texture Operations
spv::Builder::TextureParameters params = {};
params.sampler = sampledImageId;
params.coords = coordsId;
params.lod = lodId; // etc: bias, Dref, offset, gradX, gradY, component, sample, lodClamp, ...
// nonprivate, volatil, nontemporal = false
spv::Id tex = builder.createTextureCall(precision, resultType,
false/*sparse*/, false/*fetch*/, false/*proj*/, false/*gather*/, false/*noImplicit*/,
params, spv::ImageOperandsMask::MaskNone);
Decorations & Names
builder.addName(id, "myVar");
builder.addMemberName(structTy, 0, "field0");
builder.addDecoration(id, spv::Decoration::Location, 0);
builder.addDecoration(id, spv::Decoration::Binding, 2);
builder.addDecoration(id, spv::Decoration::DescriptorSet, 0);
builder.addDecoration(id, spv::Decoration::NoContraction);
builder.addDecoration(id, spv::Decoration::RelaxedPrecision);
builder.addDecoration(id, spv::Decoration::BuiltIn, (int)spv::BuiltIn::Position);
builder.addMemberDecoration(structTy, 0, spv::Decoration::Offset, 0);
builder.addMemberDecoration(structTy, 1, spv::Decoration::Offset, 16);
builder.addDecoration(id, spv::Decoration::WorkgroupSize, std::vector<unsigned>{64, 1, 1});
builder.addDecorationId(id, spv::Decoration::ArrayStrideIdEXT, strideId);
builder.addLinkageDecoration(id, "myFunc", spv::LinkageType::Export);
Barriers
builder.createControlBarrier(spv::Scope::Workgroup, spv::Scope::Device,
spv::MemorySemanticsMask::UniformMemory | spv::MemorySemanticsMask::WorkgroupMemory);
builder.createMemoryBarrier(spv::Scope::Device, spv::MemorySemanticsMask::ImageMemory);
Debug Info
SPIR-V Standard (OpLine/OpSource)
builder.setEmitSpirvDebugInfo(); // enables OpLine/OpSource tracking
builder.setDebugMainSourceFile("shader.glsl");
builder.setDebugSourceLocation(42, "shader.glsl");
builder.setSourceText(sourceText);
NonSemantic Shader Debug Info
builder.setEmitNonSemanticShaderDebugInfo(true); // also enables OpLine-style tracking
spv::Id debugType = builder.getDebugType(spirvTypeId);
builder.enterLexicalBlock(line, column);
builder.leaveLexicalBlock();
builder.setupFunctionDebugInfo(func, "myFunc", paramTypes, paramNames);
spv::Id dbgGlobal = builder.createDebugGlobalVariable(debugType, "globalVar", varId);
spv::Id dbgLocal = builder.createDebugLocalVariable(debugType, "localVar", argNumber);
spv::Id dbgDecl = builder.makeDebugDeclare(dbgLocal, ptrId);
spv::Id dbgVal = builder.makeDebugValue(dbgLocal, valueId);
Function Calls & Builtins
spv::Id result = builder.createFunctionCall(calleeFunc, {arg0, arg1, arg2});
spv::Id sqrtVal = builder.createBuiltinCall(floatTy, glsl450, GLSLstd450Sqrt, {val});
Post-Processing & Serialization
builder.postProcess(false); // prune + caps/extensions
builder.postProcessCFG(); // prune unreachable
builder.postProcessFeatures(); // add caps/extensions from instructions
builder.postProcessSamplers(); // move OpSampledImage near users
std::vector<unsigned int> spirv;
builder.dump(spirv);
spv::Disassemble(std::cout, spirv);
glslang::OutputSpvBin(spirv, "out.spv");
glslang::OutputSpvHex(spirv, "out.h", "g_spv");
Both postProcessCFG() and Function::dump() traverse physical blocks with
inReadableOrder(), which assumes structured merge roles already nest. If an
outer selection merge is also an inner arm and then branches to the inner
merge, the physical graph exits the inner construct and re-enters it. The
traversal can initially mask that invalid topology by classifying the inner
merge as dead, replacing live code with OpUnreachable, and serializing it
before its dominator. Fix the producer's physical control-flow plan: preserve
the payload blocks but rotate the adjacent merge declarations so the inner
merge physically precedes the outer merge. Do not patch serialization order or
disable post-processing/validation around an invalid graph.
OpSwitch case literals are sized by the selector's OpTypeInt, not by the
generated operand-table class alone. A selector up to 32 bits uses one literal
word; a 64-bit selector uses two low-word-first literal words followed by one
target label ID. Disassemblers and binary walkers must resolve the selector
type and consume ceil(bit_width / 32) words per case before reading the label.
Never infer case boundaries by alternating one literal word and one ID.
Treat disassembly input as untrusted. Validate each instruction-local word
count before reading operands: reject zero, undersized, or module-truncated
instructions. When resolving an OpSwitch selector, also validate the mapped
defining instruction bounds and result ID; accept OpTypeInt only with its
exact four-word layout and a width of 8, 16, 32, or 64. Validate a directly
visited OpTypeInt before reading its width operand. The disassembler's fatal
path exits the process, so malformed-input regressions must run it in a child
process and assert the deterministic nonzero exit.
IR Classes (spvIR.h)
spv::Instruction* inst = new spv::Instruction(resultId, typeId, spv::Op::OpIAdd);
inst->addIdOperand(opA);
inst->addIdOperand(opB);
spv::Block* block = new spv::Block(blockId, *function);
block->addInstruction(std::unique_ptr<spv::Instruction>(inst));
block->addLocalVariable(std::unique_ptr<spv::Instruction>(varInst));
bool terminated = block->isTerminated();
spv::Function* func = new spv::Function(funcId, retType, funcType, firstParamId, linkage, name, module);
func->addBlock(block);
func->setReturnPrecision(spv::Decoration::RelaxedPrecision);
func->addParamPrecision(0, spv::Decoration::RelaxedPrecision);
spv::Module module;
module.addFunction(func);
module.mapInstruction(inst);
spv::Instruction* found = module.getInstruction(id);
spv::Id typeId = module.getTypeId(resultId);
Key Types
| Type | Purpose |
|---|---|
spv::Builder | SPIR-V module construction |
spv::Instruction | Single SPIR-V instruction |
spv::Block | Basic block |
spv::Function | SPIR-V function |
spv::Module | Module root, ID→instruction map |
spv::Builder::If | Structured if-then-else helper |
spv::Builder::LoopBlocks | Structured loop blocks |
spv::Builder::AccessChain | L-value/R-value access chain |
spv::Builder::TextureParameters | Texture op parameters |
spv::IdImmediate | Operand: ID or immediate |
glslang::SpvOptions | GlslangToSpv options |
GlslangToSpv Patterns
From TGlslangToSpvTraverser (src/ext/glslang/SPIRV/GlslangToSpv.cpp). Common pattern: clear access chain → traverse → load/store → set R-value.
visitSymbol
builder.clearAccessChain();
// Treat spec constants, r-value parameters, and non-pointer/untyped values as r-values.
if (isRValue || rValueParameters.count(symbolId) ||
(!builder.isPointerType(builder.getTypeId(id)) && !builder.isUntypedPointer(id)))
builder.setAccessChainRValue(id);
else
builder.setAccessChainLValue(id);
spv::StorageClass sc = builder.getStorageClass(id);
if (builder.isGlobalVariable(id))
iOSet.insert(id);
builder.addExtension("SPV_GOOGLE_hlsl_functionality1");
builder.addDecorationId(id, spv::Decoration::HlslCounterBufferGOOGLE, counterId);
visitBinary (Assignment)
builder.clearAccessChain(); node->getLeft()->traverse(this);
auto lValue = builder.getAccessChain();
builder.clearAccessChain(); node->getRight()->traverse(this);
spv::Id rValue = accessChainLoad(node->getRight()->getType());
builder.setAccessChain(lValue);
multiTypeStore(node->getLeft()->getType(), rValue);
builder.clearAccessChain(); builder.setAccessChainRValue(rValue);
visitBinary (Array/Vector Index)
// zero-extend narrow uint indexes to 32-bit
if (builder.isUintType(indexType) && builder.getScalarTypeWidth(indexType) < 32)
index = builder.createUnaryOp(spv::Op::OpUConvert, builder.makeUintType(32), index);
builder.accessChainPush(index, coherentFlags, alignment);
visitBinary (Swizzle)
builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()),
coherentFlags, alignment);
visitUnary (Inc/Dec)
spv::Id operand = builder.accessChainGetLValue();
spv::Id one = builder.makeIntConstant(1);
spv::Id result = builder.createBinOp(op, type, operand, one);
builder.accessChainStore(result, ...);
builder.clearAccessChain(); builder.setAccessChainRValue(result);
visitUnary (Builtin / NoResult / ArrayLength)
// Builtin
spv::Id result = builder.createBuiltinCall(resultType(), glsl450, opcode, {operand});
// No-result
builder.createNoResultOp(spv::Op::OpKill);
builder.createNoResultOp(spv::Op::OpTerminateInvocation);
builder.createNoResultOp(spv::Op::OpDemoteToHelperInvocationEXT);
builder.createNoResultOp(spv::Op::OpAssumeTrueKHR, operand);
// Array length
spv::Id len = builder.createArrayLength(builder.accessChainGetLValue(), member, bits);
len = builder.createUnaryOp(spv::Op::OpBitcast, builder.makeIntType(bits), len);
// Cooperative matrix/vector
spv::Id lenKHR = builder.createCooperativeMatrixLengthKHR(typeId);
spv::Id lenNV = builder.createCooperativeMatrixLengthNV(typeId);
spv::Id lenVec = builder.getCooperativeVectorNumComponents(typeId);
// Tensor
spv::Id layout = builder.createOp(spv::Op::OpCreateTensorLayoutNV, resultType(), {});
spv::Id view = builder.createOp(spv::Op::OpCreateTensorViewNV, resultType(), {});
visitAggregate
// Function entry/leave
builder.setBuildPoint(shaderEntry->getLastBlock());
builder.enterFunction(shaderEntry); /* body */ builder.leaveFunction();
// Function call
spv::Id result = builder.createFunctionCall(callee, arguments);
// Constructors
spv::Id c = builder.createConstructor(precision, arguments, resultType());
spv::Id m = builder.createMatrixConstructor(precision, arguments, resultType());
// Builtin
spv::Id r = builder.createBuiltinCall(resultType(), extInst, opcode, arguments);
// Texture
spv::Builder::TextureParameters params = {sampledImageId, coordsId, /*...*/};
spv::Id tex = builder.createTextureCall(precision, resultType(), sparse, fetch, proj, gather, noImplicit, params, mask);
// Sampled image
spv::Id sampled = builder.createOp(spv::Op::OpSampledImage, resultType(), {imageId, samplerId});
// Cooperative matrix conversion
spv::Id coop = builder.createCooperativeMatrixConversion(resultType(), arguments[0]);
// Variable
spv::Id var = builder.createVariable(precision, spv::StorageClass::Function, type, name, init);
// Load/store
spv::Id loaded = builder.createLoad(ptrId, precision);
builder.createStore(valueId, ptrId);
// Debug scopes
builder.enterLexicalBlock(loc.line, loc.column); /* body */ builder.leaveLexicalBlock();
visitSelection
// Scalar ternary
spv::Id result = builder.createTriOp(spv::Op::OpSelect, resultType, cond, trueVal, falseVal);
// Vector selection: for SPIR-V < 1.4 smear the scalar condition to the vector width;
// for SPIR-V >= 1.4 OpSelect accepts a scalar condition directly.
if (builder.getSpvVersion() < spv::Spv_1_4 && builder.isVector(trueVal)) {
cond = builder.smearScalar(precision, cond,
builder.makeVectorType(builder.makeBoolType(),
builder.getNumComponents(trueVal)));
}
// If aggregate decorations cause type mismatches, normalize with OpCopyLogical.
if (builder.getTypeId(trueVal) != resultType)
trueVal = builder.createUnaryOp(spv::Op::OpCopyLogical, resultType, trueVal);
if (builder.getTypeId(falseVal) != resultType)
falseVal = builder.createUnaryOp(spv::Op::OpCopyLogical, resultType, falseVal);
spv::Id result = builder.createTriOp(spv::Op::OpSelect, resultType, cond, trueVal, falseVal);
visitSwitch
std::vector<int> caseValues = {0,1,2}, valueToSegment = {0,1,2};
builder.makeSwitch(selectorId, spv::SelectionControlMask::MaskNone, 4, caseValues, valueToSegment, 3, segmentBB);
builder.nextSwitchSegment(segmentBB, 0); /* case 0 */ builder.addSwitchBreak(false);
// ...
builder.endSwitch(segmentBB);
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 1k
- Forks
- 108
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
glslang- Source
- github.com/luisagroup/luisacompute