@@ -4803,6 +4803,7 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
4803
4803
Op :: ConstantFalse | Op :: SpecConstantFalse => {
4804
4804
self . parse_bool_constant ( inst, false , & mut module)
4805
4805
}
4806
+ Op :: SpecConstantOp => self . parse_spec_constant_op ( inst, & mut module) ,
4806
4807
Op :: Variable => self . parse_global_variable ( inst, & mut module) ,
4807
4808
Op :: Function => {
4808
4809
self . switch ( ModuleState :: Function , inst. op ) ?;
@@ -5899,6 +5900,296 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
5899
5900
self . insert_parsed_constant ( module, id, type_id, ty, init, span)
5900
5901
}
5901
5902
5903
+ fn parse_spec_constant_op (
5904
+ & mut self ,
5905
+ inst : Instruction ,
5906
+ module : & mut crate :: Module ,
5907
+ ) -> Result < ( ) , Error > {
5908
+ use spirv:: Op ;
5909
+
5910
+ let start = self . data_offset ;
5911
+ self . switch ( ModuleState :: Type , inst. op ) ?;
5912
+ inst. expect_at_least ( 4 ) ?;
5913
+
5914
+ let result_type_id = self . next ( ) ?;
5915
+ let result_id = self . next ( ) ?;
5916
+ let opcode_word = self . next ( ) ?;
5917
+
5918
+ let type_lookup = self . lookup_type . lookup ( result_type_id) ?;
5919
+ let ty = type_lookup. handle ;
5920
+ let span = self . span_from_with_op ( start) ;
5921
+
5922
+ let opcode = Op :: from_u32 ( opcode_word) . ok_or ( Error :: UnsupportedInstruction (
5923
+ self . state ,
5924
+ Op :: SpecConstantOp ,
5925
+ ) ) ?;
5926
+
5927
+ let mut get_const_expr =
5928
+ |frontend : & Self , const_id : spirv:: Word | -> Result < Handle < crate :: Expression > , Error > {
5929
+ let lookup = frontend. lookup_constant . lookup ( const_id) ?;
5930
+ match lookup. inner {
5931
+ // Wrap regular constants in overrides to avoid creating Expression::Constant
5932
+ // nodes, which would mark the entire expression tree as Const and fail
5933
+ // validation for complex operations (Binary, As, Math) in override
5934
+ // initializers.
5935
+ //
5936
+ // The downside is, that unused intermediate constants will get created, which
5937
+ // I would like to avoid.
5938
+ Constant :: Constant ( const_handle) => {
5939
+ let const_init = module. constants [ const_handle] . init ;
5940
+ let const_ty = module. constants [ const_handle] . ty ;
5941
+ let wrapper_override = crate :: Override {
5942
+ name : Some ( format ! ( "_spec_const_op_const_{const_id}" ) ) ,
5943
+ id : None ,
5944
+ ty : const_ty,
5945
+ init : Some ( const_init) ,
5946
+ } ;
5947
+ let override_handle = module. overrides . append ( wrapper_override, span) ;
5948
+ Ok ( module
5949
+ . global_expressions
5950
+ . append ( crate :: Expression :: Override ( override_handle) , span) )
5951
+ }
5952
+ Constant :: Override ( _) => Ok ( module
5953
+ . global_expressions
5954
+ . append ( lookup. inner . to_expr ( ) , span) ) ,
5955
+ }
5956
+ } ;
5957
+
5958
+ let init = match opcode {
5959
+ Op :: SConvert | Op :: UConvert | Op :: FConvert => {
5960
+ let value_id = self . next ( ) ?;
5961
+ let value_expr = get_const_expr ( self , value_id) ?;
5962
+
5963
+ let scalar = match module. types [ ty] . inner {
5964
+ crate :: TypeInner :: Scalar ( scalar)
5965
+ | crate :: TypeInner :: Vector { scalar, .. }
5966
+ | crate :: TypeInner :: Matrix { scalar, .. } => scalar,
5967
+ _ => return Err ( Error :: InvalidAsType ( ty) ) ,
5968
+ } ;
5969
+
5970
+ module. global_expressions . append (
5971
+ crate :: Expression :: As {
5972
+ expr : value_expr,
5973
+ kind : scalar. kind ,
5974
+ convert : Some ( scalar. width ) ,
5975
+ } ,
5976
+ span,
5977
+ )
5978
+ }
5979
+
5980
+ Op :: SNegate | Op :: Not | Op :: LogicalNot => {
5981
+ let value_id = self . next ( ) ?;
5982
+ let value_expr = get_const_expr ( self , value_id) ?;
5983
+
5984
+ let op = match opcode {
5985
+ Op :: SNegate => crate :: UnaryOperator :: Negate ,
5986
+ Op :: Not => crate :: UnaryOperator :: BitwiseNot ,
5987
+ Op :: LogicalNot => crate :: UnaryOperator :: LogicalNot ,
5988
+ _ => unreachable ! ( ) ,
5989
+ } ;
5990
+
5991
+ module. global_expressions . append (
5992
+ crate :: Expression :: Unary {
5993
+ op,
5994
+ expr : value_expr,
5995
+ } ,
5996
+ span,
5997
+ )
5998
+ }
5999
+
6000
+ Op :: IAdd
6001
+ | Op :: ISub
6002
+ | Op :: IMul
6003
+ | Op :: UDiv
6004
+ | Op :: SDiv
6005
+ | Op :: SRem
6006
+ | Op :: UMod
6007
+ | Op :: BitwiseOr
6008
+ | Op :: BitwiseXor
6009
+ | Op :: BitwiseAnd
6010
+ | Op :: ShiftLeftLogical
6011
+ | Op :: ShiftRightLogical
6012
+ | Op :: ShiftRightArithmetic
6013
+ | Op :: LogicalOr
6014
+ | Op :: LogicalAnd
6015
+ | Op :: LogicalEqual
6016
+ | Op :: LogicalNotEqual
6017
+ | Op :: IEqual
6018
+ | Op :: INotEqual
6019
+ | Op :: ULessThan
6020
+ | Op :: SLessThan
6021
+ | Op :: UGreaterThan
6022
+ | Op :: SGreaterThan
6023
+ | Op :: ULessThanEqual
6024
+ | Op :: SLessThanEqual
6025
+ | Op :: UGreaterThanEqual
6026
+ | Op :: SGreaterThanEqual => {
6027
+ let left_id = self . next ( ) ?;
6028
+ let right_id = self . next ( ) ?;
6029
+ let left_expr = get_const_expr ( self , left_id) ?;
6030
+ let right_expr = get_const_expr ( self , right_id) ?;
6031
+
6032
+ let op = match opcode {
6033
+ Op :: IAdd => crate :: BinaryOperator :: Add ,
6034
+ Op :: ISub => crate :: BinaryOperator :: Subtract ,
6035
+ Op :: IMul => crate :: BinaryOperator :: Multiply ,
6036
+ Op :: UDiv | Op :: SDiv => crate :: BinaryOperator :: Divide ,
6037
+ Op :: SRem | Op :: UMod => crate :: BinaryOperator :: Modulo ,
6038
+ Op :: BitwiseOr => crate :: BinaryOperator :: InclusiveOr ,
6039
+ Op :: BitwiseXor => crate :: BinaryOperator :: ExclusiveOr ,
6040
+ Op :: BitwiseAnd => crate :: BinaryOperator :: And ,
6041
+ Op :: ShiftLeftLogical => crate :: BinaryOperator :: ShiftLeft ,
6042
+ Op :: ShiftRightLogical | Op :: ShiftRightArithmetic => {
6043
+ crate :: BinaryOperator :: ShiftRight
6044
+ }
6045
+ Op :: LogicalOr => crate :: BinaryOperator :: LogicalOr ,
6046
+ Op :: LogicalAnd => crate :: BinaryOperator :: LogicalAnd ,
6047
+ Op :: LogicalEqual => crate :: BinaryOperator :: Equal ,
6048
+ Op :: LogicalNotEqual => crate :: BinaryOperator :: NotEqual ,
6049
+ Op :: IEqual => crate :: BinaryOperator :: Equal ,
6050
+ Op :: INotEqual => crate :: BinaryOperator :: NotEqual ,
6051
+ Op :: ULessThan | Op :: SLessThan => crate :: BinaryOperator :: Less ,
6052
+ Op :: UGreaterThan | Op :: SGreaterThan => crate :: BinaryOperator :: Greater ,
6053
+ Op :: ULessThanEqual | Op :: SLessThanEqual => crate :: BinaryOperator :: LessEqual ,
6054
+ Op :: UGreaterThanEqual | Op :: SGreaterThanEqual => {
6055
+ crate :: BinaryOperator :: GreaterEqual
6056
+ }
6057
+ _ => unreachable ! ( ) ,
6058
+ } ;
6059
+
6060
+ module. global_expressions . append (
6061
+ crate :: Expression :: Binary {
6062
+ op,
6063
+ left : left_expr,
6064
+ right : right_expr,
6065
+ } ,
6066
+ span,
6067
+ )
6068
+ }
6069
+
6070
+ Op :: SMod => {
6071
+ // x - y * int(floor(float(x) / float(y)))
6072
+
6073
+ let left_id = self . next ( ) ?;
6074
+ let right_id = self . next ( ) ?;
6075
+ let left = get_const_expr ( self , left_id) ?;
6076
+ let right = get_const_expr ( self , right_id) ?;
6077
+
6078
+ let scalar = match module. types [ ty] . inner {
6079
+ crate :: TypeInner :: Scalar ( scalar) => scalar,
6080
+ crate :: TypeInner :: Vector { scalar, .. } => scalar,
6081
+ _ => return Err ( Error :: InvalidAsType ( ty) ) ,
6082
+ } ;
6083
+
6084
+ let left_cast = module. global_expressions . append (
6085
+ crate :: Expression :: As {
6086
+ expr : left,
6087
+ kind : crate :: ScalarKind :: Float ,
6088
+ convert : Some ( scalar. width ) ,
6089
+ } ,
6090
+ span,
6091
+ ) ;
6092
+ let right_cast = module. global_expressions . append (
6093
+ crate :: Expression :: As {
6094
+ expr : right,
6095
+ kind : crate :: ScalarKind :: Float ,
6096
+ convert : Some ( scalar. width ) ,
6097
+ } ,
6098
+ span,
6099
+ ) ;
6100
+ let div = module. global_expressions . append (
6101
+ crate :: Expression :: Binary {
6102
+ op : crate :: BinaryOperator :: Divide ,
6103
+ left : left_cast,
6104
+ right : right_cast,
6105
+ } ,
6106
+ span,
6107
+ ) ;
6108
+ let floor = module. global_expressions . append (
6109
+ crate :: Expression :: Math {
6110
+ fun : crate :: MathFunction :: Floor ,
6111
+ arg : div,
6112
+ arg1 : None ,
6113
+ arg2 : None ,
6114
+ arg3 : None ,
6115
+ } ,
6116
+ span,
6117
+ ) ;
6118
+ let cast = module. global_expressions . append (
6119
+ crate :: Expression :: As {
6120
+ expr : floor,
6121
+ kind : scalar. kind ,
6122
+ convert : Some ( scalar. width ) ,
6123
+ } ,
6124
+ span,
6125
+ ) ;
6126
+ let mult = module. global_expressions . append (
6127
+ crate :: Expression :: Binary {
6128
+ op : crate :: BinaryOperator :: Multiply ,
6129
+ left : cast,
6130
+ right,
6131
+ } ,
6132
+ span,
6133
+ ) ;
6134
+ module. global_expressions . append (
6135
+ crate :: Expression :: Binary {
6136
+ op : crate :: BinaryOperator :: Subtract ,
6137
+ left,
6138
+ right : mult,
6139
+ } ,
6140
+ span,
6141
+ )
6142
+ }
6143
+
6144
+ Op :: Select => {
6145
+ let condition_id = self . next ( ) ?;
6146
+ let o1_id = self . next ( ) ?;
6147
+ let o2_id = self . next ( ) ?;
6148
+
6149
+ let cond = get_const_expr ( self , condition_id) ?;
6150
+ let o1 = get_const_expr ( self , o1_id) ?;
6151
+ let o2 = get_const_expr ( self , o2_id) ?;
6152
+
6153
+ module. global_expressions . append (
6154
+ crate :: Expression :: Select {
6155
+ condition : cond,
6156
+ accept : o1,
6157
+ reject : o2,
6158
+ } ,
6159
+ span,
6160
+ )
6161
+ }
6162
+
6163
+ Op :: VectorShuffle | Op :: CompositeExtract | Op :: CompositeInsert | Op :: QuantizeToF16 => {
6164
+ // Nothing stops us from implementing these cases in general.
6165
+ // I just couldn't get them to work properly.
6166
+ return Err ( Error :: UnsupportedSpecConstantOp ( opcode) ) ;
6167
+ }
6168
+
6169
+ _ => return Err ( Error :: InvalidSpecConstantOp ( opcode) ) ,
6170
+ } ;
6171
+
6172
+ // IMPORTANT: Overrides must have either a name or an id to be processed correctly
6173
+ // by process_overrides(). OpSpecConstantOp results don't have a SpecId (they're
6174
+ // not user-overridable), so we assign them a name based on the result_id.
6175
+ let op_override = crate :: Override {
6176
+ name : Some ( format ! ( "_spec_const_op_{result_id}" ) ) ,
6177
+ id : None ,
6178
+ ty,
6179
+ init : Some ( init) ,
6180
+ } ;
6181
+
6182
+ self . lookup_constant . insert (
6183
+ result_id,
6184
+ LookupConstant {
6185
+ inner : Constant :: Override ( module. overrides . append ( op_override, span) ) ,
6186
+ type_id : result_type_id,
6187
+ } ,
6188
+ ) ;
6189
+
6190
+ Ok ( ( ) )
6191
+ }
6192
+
5902
6193
fn insert_parsed_constant (
5903
6194
& mut self ,
5904
6195
module : & mut crate :: Module ,
0 commit comments