@@ -4,12 +4,13 @@ use crate::spirv_type::SpirvType;
44use crate :: symbols:: Symbols ;
55use crate :: target:: SpirvTarget ;
66use crate :: target_feature:: TargetFeature ;
7- use rspirv:: dr:: { Block , Builder , Module , Operand } ;
7+ use rspirv:: dr:: { Block , Builder , Instruction , Module , Operand } ;
88use rspirv:: spirv:: {
99 AddressingModel , Capability , MemoryModel , Op , SourceLanguage , StorageClass , Word ,
1010} ;
1111use rspirv:: { binary:: Assemble , binary:: Disassemble } ;
1212use rustc_arena:: DroplessArena ;
13+ use rustc_codegen_ssa:: traits:: ConstMethods as _;
1314use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
1415use rustc_data_structures:: sync:: Lrc ;
1516use rustc_middle:: bug;
@@ -18,6 +19,7 @@ use rustc_middle::ty::TyCtxt;
1819use rustc_span:: source_map:: SourceMap ;
1920use rustc_span:: symbol:: Symbol ;
2021use rustc_span:: { FileName , FileNameDisplayPreference , SourceFile , Span , DUMMY_SP } ;
22+ use rustc_target:: abi:: Size ;
2123use std:: assert_matches:: assert_matches;
2224use std:: cell:: { RefCell , RefMut } ;
2325use std:: hash:: { Hash , Hasher } ;
@@ -221,13 +223,8 @@ impl SpirvValueExt for Word {
221223
222224#[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
223225pub enum SpirvConst < ' a , ' tcx > {
224- U32 ( u32 ) ,
225- U64 ( u64 ) ,
226- /// f32 isn't hash, so store bits
227- F32 ( u32 ) ,
228- /// f64 isn't hash, so store bits
229- F64 ( u64 ) ,
230- Bool ( bool ) ,
226+ /// Constants of boolean, integer or floating-point type (up to 128-bit).
227+ Scalar ( u128 ) ,
231228
232229 Null ,
233230 Undef ,
@@ -273,11 +270,7 @@ impl<'tcx> SpirvConst<'_, 'tcx> {
273270
274271 match self {
275272 // FIXME(eddyb) these are all noop cases, could they be automated?
276- SpirvConst :: U32 ( v) => SpirvConst :: U32 ( v) ,
277- SpirvConst :: U64 ( v) => SpirvConst :: U64 ( v) ,
278- SpirvConst :: F32 ( v) => SpirvConst :: F32 ( v) ,
279- SpirvConst :: F64 ( v) => SpirvConst :: F64 ( v) ,
280- SpirvConst :: Bool ( v) => SpirvConst :: Bool ( v) ,
273+ SpirvConst :: Scalar ( v) => SpirvConst :: Scalar ( v) ,
281274 SpirvConst :: Null => SpirvConst :: Null ,
282275 SpirvConst :: Undef => SpirvConst :: Undef ,
283276 SpirvConst :: ZombieUndefForFnAddr => SpirvConst :: ZombieUndefForFnAddr ,
@@ -570,8 +563,26 @@ impl<'tcx> BuilderSpirv<'tcx> {
570563 val : SpirvConst < ' _ , ' tcx > ,
571564 cx : & CodegenCx < ' tcx > ,
572565 ) -> SpirvValue {
566+ let scalar_ty = match val {
567+ SpirvConst :: Scalar ( _) => Some ( cx. lookup_type ( ty) ) ,
568+ _ => None ,
569+ } ;
570+
571+ // HACK(eddyb) this is done so late (just before interning `val`) to
572+ // minimize any potential misuse from direct `def_constant` calls.
573+ let val = match ( val, scalar_ty) {
574+ ( SpirvConst :: Scalar ( val) , Some ( SpirvType :: Integer ( bits, signed) ) ) => {
575+ let size = Size :: from_bits ( bits) ;
576+ SpirvConst :: Scalar ( if signed {
577+ size. sign_extend ( val)
578+ } else {
579+ size. truncate ( val)
580+ } )
581+ }
582+ _ => val,
583+ } ;
584+
573585 let val_with_type = WithType { ty, val } ;
574- let mut builder = self . builder ( BuilderCursor :: default ( ) ) ;
575586 if let Some ( entry) = self . const_to_id . borrow ( ) . get ( & val_with_type) {
576587 // FIXME(eddyb) deduplicate this `if`-`else` and its other copies.
577588 let kind = if entry. legal . is_ok ( ) {
@@ -582,16 +593,99 @@ impl<'tcx> BuilderSpirv<'tcx> {
582593 return SpirvValue { kind, ty } ;
583594 }
584595 let val = val_with_type. val ;
596+
597+ // FIXME(eddyb) make this an extension method on `rspirv::dr::Builder`?
598+ let const_op = |builder : & mut Builder , op, lhs, maybe_rhs : Option < _ > | {
599+ // HACK(eddyb) remove after `OpSpecConstantOp` support gets added to SPIR-T.
600+ let spirt_has_const_op = false ;
601+
602+ if !spirt_has_const_op {
603+ let zombie = builder. undef ( ty, None ) ;
604+ cx. zombie_with_span (
605+ zombie,
606+ DUMMY_SP ,
607+ & format ! ( "unsupported constant of type `{}`" , cx. debug_type( ty) ) ,
608+ ) ;
609+ return zombie;
610+ }
611+
612+ let id = builder. id ( ) ;
613+ builder
614+ . module_mut ( )
615+ . types_global_values
616+ . push ( Instruction :: new (
617+ Op :: SpecConstantOp ,
618+ Some ( ty) ,
619+ Some ( id) ,
620+ [
621+ Operand :: LiteralSpecConstantOpInteger ( op) ,
622+ Operand :: IdRef ( lhs) ,
623+ ]
624+ . into_iter ( )
625+ . chain ( maybe_rhs. map ( Operand :: IdRef ) )
626+ . collect ( ) ,
627+ ) ) ;
628+ id
629+ } ;
630+
631+ let mut builder = self . builder ( BuilderCursor :: default ( ) ) ;
585632 let id = match val {
586- SpirvConst :: U32 ( v) | SpirvConst :: F32 ( v) => builder. constant_bit32 ( ty, v) ,
587- SpirvConst :: U64 ( v) | SpirvConst :: F64 ( v) => builder. constant_bit64 ( ty, v) ,
588- SpirvConst :: Bool ( v) => {
589- if v {
590- builder. constant_true ( ty)
591- } else {
592- builder. constant_false ( ty)
633+ SpirvConst :: Scalar ( v) => match scalar_ty. unwrap ( ) {
634+ SpirvType :: Integer ( ..=32 , _) | SpirvType :: Float ( ..=32 ) => {
635+ builder. constant_bit32 ( ty, v as u32 )
593636 }
594- }
637+ SpirvType :: Integer ( 64 , _) | SpirvType :: Float ( 64 ) => {
638+ builder. constant_bit64 ( ty, v as u64 )
639+ }
640+ SpirvType :: Integer ( 128 , false ) => {
641+ // HACK(eddyb) avoid borrow conflicts.
642+ drop ( builder) ;
643+
644+ let const_64_u32_id = cx. const_u32 ( 64 ) . def_cx ( cx) ;
645+ let [ lo_id, hi_id] =
646+ [ v as u64 , ( v >> 64 ) as u64 ] . map ( |half| cx. const_u64 ( half) . def_cx ( cx) ) ;
647+
648+ builder = self . builder ( BuilderCursor :: default ( ) ) ;
649+ let mut const_op =
650+ |op, lhs, maybe_rhs| const_op ( & mut builder, op, lhs, maybe_rhs) ;
651+ let [ lo_u128_id, hi_shifted_u128_id] =
652+ [ ( lo_id, None ) , ( hi_id, Some ( const_64_u32_id) ) ] . map (
653+ |( half_u64_id, shift) | {
654+ let mut half_u128_id = const_op ( Op :: UConvert , half_u64_id, None ) ;
655+ if let Some ( shift_amount_id) = shift {
656+ half_u128_id = const_op (
657+ Op :: ShiftLeftLogical ,
658+ half_u128_id,
659+ Some ( shift_amount_id) ,
660+ ) ;
661+ }
662+ half_u128_id
663+ } ,
664+ ) ;
665+ const_op ( Op :: BitwiseOr , lo_u128_id, Some ( hi_shifted_u128_id) )
666+ }
667+ SpirvType :: Integer ( 128 , true ) | SpirvType :: Float ( 128 ) => {
668+ // HACK(eddyb) avoid borrow conflicts.
669+ drop ( builder) ;
670+
671+ let v_u128_id = cx. const_u128 ( v) . def_cx ( cx) ;
672+
673+ builder = self . builder ( BuilderCursor :: default ( ) ) ;
674+ const_op ( & mut builder, Op :: Bitcast , v_u128_id, None )
675+ }
676+ SpirvType :: Bool => match v {
677+ 0 => builder. constant_false ( ty) ,
678+ 1 => builder. constant_true ( ty) ,
679+ _ => cx
680+ . tcx
681+ . dcx ( )
682+ . fatal ( format ! ( "invalid constant value for bool: {v}" ) ) ,
683+ } ,
684+ other => cx. tcx . dcx ( ) . fatal ( format ! (
685+ "SpirvConst::Scalar does not support type {}" ,
686+ other. debug( ty, cx)
687+ ) ) ,
688+ } ,
595689
596690 SpirvConst :: Null => builder. constant_null ( ty) ,
597691 SpirvConst :: Undef
@@ -606,11 +700,7 @@ impl<'tcx> BuilderSpirv<'tcx> {
606700 } ;
607701 #[ allow( clippy:: match_same_arms) ]
608702 let legal = match val {
609- SpirvConst :: U32 ( _)
610- | SpirvConst :: U64 ( _)
611- | SpirvConst :: F32 ( _)
612- | SpirvConst :: F64 ( _)
613- | SpirvConst :: Bool ( _) => Ok ( ( ) ) ,
703+ SpirvConst :: Scalar ( _) => Ok ( ( ) ) ,
614704
615705 SpirvConst :: Null => {
616706 // FIXME(eddyb) check that the type supports `OpConstantNull`.
@@ -712,10 +802,9 @@ impl<'tcx> BuilderSpirv<'tcx> {
712802 }
713803 }
714804
715- pub fn lookup_const_u64 ( & self , def : SpirvValue ) -> Option < u64 > {
805+ pub fn lookup_const_scalar ( & self , def : SpirvValue ) -> Option < u128 > {
716806 match self . lookup_const ( def) ? {
717- SpirvConst :: U32 ( v) => Some ( v as u64 ) ,
718- SpirvConst :: U64 ( v) => Some ( v) ,
807+ SpirvConst :: Scalar ( v) => Some ( v) ,
719808 _ => None ,
720809 }
721810 }
0 commit comments