@@ -199,12 +199,22 @@ void WriteStrings (GeneratorWriteContext context)
199
199
}
200
200
201
201
foreach ( LlvmIrStringVariable info in group . Strings ) {
202
- string s = QuoteString ( ( string ) info . Value , out ulong size ) ;
202
+ string s = QuoteString ( info , out ulong size ) ;
203
203
204
- WriteGlobalVariableStart ( context , info ) ;
204
+ if ( ! info . IsConstantStringLiteral ) {
205
+ WriteCommentLine ( context , $ " '{ info . Value } '") ;
206
+ }
207
+
208
+ WriteGlobalVariableName ( context , info ) ;
209
+
210
+ // Strings must always be local symbols, global variables will point to them
211
+ WriteVariableOptions ( context , LlvmIrVariableOptions . LocalString ) ;
205
212
context . Output . Write ( '[' ) ;
206
213
context . Output . Write ( size . ToString ( CultureInfo . InvariantCulture ) ) ;
207
- context . Output . Write ( " x i8] c" ) ;
214
+ context . Output . Write ( $ " x { info . IrType } ] ") ;
215
+ if ( info . IsConstantStringLiteral ) {
216
+ context . Output . Write ( 'c' ) ;
217
+ }
208
218
context . Output . Write ( s ) ;
209
219
context . Output . Write ( ", align " ) ;
210
220
context . Output . WriteLine ( target . GetAggregateAlignment ( 1 , size ) . ToString ( CultureInfo . InvariantCulture ) ) ;
@@ -246,23 +256,37 @@ void WriteGlobalVariables (GeneratorWriteContext context)
246
256
}
247
257
}
248
258
249
- void WriteGlobalVariableStart ( GeneratorWriteContext context , LlvmIrGlobalVariable variable )
259
+ void WriteGlobalVariableName ( GeneratorWriteContext context , LlvmIrGlobalVariable variable )
250
260
{
251
261
if ( ! String . IsNullOrEmpty ( variable . Comment ) ) {
252
262
WriteCommentLine ( context , variable . Comment ) ;
253
263
}
254
264
context . Output . Write ( '@' ) ;
255
265
context . Output . Write ( variable . Name ) ;
256
266
context . Output . Write ( " = " ) ;
267
+ }
257
268
258
- LlvmIrVariableOptions options = variable . Options ?? LlvmIrGlobalVariable . DefaultOptions ;
269
+ void WriteVariableOptions ( GeneratorWriteContext context , LlvmIrVariableOptions options )
270
+ {
259
271
WriteLinkage ( context , options . Linkage ) ;
260
272
WritePreemptionSpecifier ( context , options . RuntimePreemption ) ;
261
273
WriteVisibility ( context , options . Visibility ) ;
262
274
WriteAddressSignificance ( context , options . AddressSignificance ) ;
263
275
WriteWritability ( context , options . Writability ) ;
264
276
}
265
277
278
+ void WriteVariableOptions ( GeneratorWriteContext context , LlvmIrGlobalVariable variable , LlvmIrVariableOptions ? defaultOptions = null )
279
+ {
280
+ LlvmIrVariableOptions options = variable . Options ?? defaultOptions ?? LlvmIrGlobalVariable . DefaultOptions ;
281
+ WriteVariableOptions ( context , options ) ;
282
+ }
283
+
284
+ void WriteGlobalVariableStart ( GeneratorWriteContext context , LlvmIrGlobalVariable variable )
285
+ {
286
+ WriteGlobalVariableName ( context , variable ) ;
287
+ WriteVariableOptions ( context , variable , LlvmIrGlobalVariable . DefaultOptions ) ;
288
+ }
289
+
266
290
void WriteGlobalVariable ( GeneratorWriteContext context , LlvmIrGlobalVariable variable )
267
291
{
268
292
if ( ! context . InVariableGroup ) {
@@ -319,13 +343,22 @@ void WriteTypeAndValue (GeneratorWriteContext context, LlvmIrVariable variable,
319
343
throw new InvalidOperationException ( $ "Internal error: variable '{ variable . Name } '' of type { variable . Type } must not have a null value") ;
320
344
}
321
345
322
- if ( valueType != variable . Type && ! LlvmIrModule . NameValueArrayType . IsAssignableFrom ( variable . Type ) ) {
346
+ if ( ! IsValueAssignableFrom ( valueType , variable ) && ! IsValueAssignableFrom ( LlvmIrModule . NameValueArrayType , variable ) ) {
323
347
throw new InvalidOperationException ( $ "Internal error: variable type '{ variable . Type } ' is different to its value type, '{ valueType } '") ;
324
348
}
325
349
326
350
WriteValue ( context , valueType , variable ) ;
327
351
}
328
352
353
+ bool IsValueAssignableFrom ( Type valueType , LlvmIrVariable variable )
354
+ {
355
+ if ( valueType != typeof ( string ) && valueType != typeof ( StringHolder ) ) {
356
+ return valueType . IsAssignableFrom ( variable . Type ) ;
357
+ }
358
+
359
+ return variable . Type == typeof ( string ) || variable . Type == typeof ( StringHolder ) ;
360
+ }
361
+
329
362
ulong GetAggregateValueElementCount ( GeneratorWriteContext context , LlvmIrVariable variable ) => GetAggregateValueElementCount ( context , variable . Type , variable . Value , variable as LlvmIrGlobalVariable ) ;
330
363
331
364
ulong GetAggregateValueElementCount ( GeneratorWriteContext context , Type type , object ? value , LlvmIrGlobalVariable ? globalVariable = null )
@@ -560,7 +593,7 @@ void WriteInlineArray (GeneratorWriteContext context, byte[] bytes, bool encodeA
560
593
{
561
594
if ( encodeAsASCII ) {
562
595
context . Output . Write ( 'c' ) ;
563
- context . Output . Write ( QuoteString ( bytes , bytes . Length , out _ , nullTerminated : false ) ) ;
596
+ context . Output . Write ( QuoteUtf8String ( bytes , bytes . Length , out _ , nullTerminated : false ) ) ;
564
597
return ;
565
598
}
566
599
@@ -616,7 +649,7 @@ void WriteValue (GeneratorWriteContext context, StructureInstance structInstance
616
649
return ;
617
650
}
618
651
619
- WriteValue ( context , smi . MemberType , value ) ;
652
+ WriteValue ( context , smi . MemberType , value , smi . Info . GetStringEncoding ( context . TypeCache ) ) ;
620
653
}
621
654
622
655
bool WriteNativePointerValue ( GeneratorWriteContext context , StructureInstance si , StructureMemberInfo smi , object ? value )
@@ -670,7 +703,7 @@ string ToHex (BasicType basicTypeDesc, Type type, object? value)
670
703
return $ "{ ( basicTypeDesc . IsUnsigned ? prefixUnsigned : prefixSigned ) } 0x{ hex } ";
671
704
}
672
705
673
- void WriteValue ( GeneratorWriteContext context , Type type , object ? value )
706
+ void WriteValue ( GeneratorWriteContext context , Type type , object ? value , LlvmIrStringEncoding stringEncoding = LlvmIrStringEncoding . UTF8 )
674
707
{
675
708
if ( value is LlvmIrVariable variableRef ) {
676
709
context . Output . Write ( variableRef . Reference ) ;
@@ -710,13 +743,13 @@ void WriteValue (GeneratorWriteContext context, Type type, object? value)
710
743
return ;
711
744
}
712
745
713
- if ( type == typeof ( string ) ) {
746
+ if ( type == typeof ( string ) || type == typeof ( StringHolder ) ) {
714
747
if ( value == null ) {
715
748
context . Output . Write ( "null" ) ;
716
749
return ;
717
750
}
718
751
719
- LlvmIrStringVariable sv = context . Module . LookupRequiredVariableForString ( ( string ) value ) ;
752
+ LlvmIrStringVariable sv = context . Module . LookupRequiredVariableForString ( StringHolder . AsHolder ( value , stringEncoding ) ) ;
720
753
context . Output . Write ( sv . Reference ) ;
721
754
return ;
722
755
}
@@ -775,7 +808,7 @@ void WriteStructureValue (GeneratorWriteContext context, StructureInstance? inst
775
808
string ? comment = info . GetCommentFromProvider ( smi , instance ) ;
776
809
if ( String . IsNullOrEmpty ( comment ) ) {
777
810
var sb = new StringBuilder ( " " ) ;
778
- sb . Append ( MapManagedTypeToNative ( smi ) ) ;
811
+ sb . Append ( MapManagedTypeToNative ( context , smi ) ) ;
779
812
sb . Append ( ' ' ) ;
780
813
sb . Append ( smi . Info . Name ) ;
781
814
comment = sb . ToString ( ) ;
@@ -1460,8 +1493,12 @@ public static string MapManagedTypeToNative (Type type)
1460
1493
return type . GetShortName ( ) ;
1461
1494
}
1462
1495
1463
- static string MapManagedTypeToNative ( StructureMemberInfo smi )
1496
+ static string MapManagedTypeToNative ( GeneratorWriteContext context , StructureMemberInfo smi )
1464
1497
{
1498
+ if ( smi . Info . IsUnicodeString ( context . TypeCache ) ) {
1499
+ return "char16_t*" ;
1500
+ }
1501
+
1465
1502
string nativeType = MapManagedTypeToNative ( smi . MemberType ) ;
1466
1503
// Silly, but effective
1467
1504
if ( nativeType [ nativeType . Length - 1 ] == '*' ) {
@@ -1487,8 +1524,9 @@ static string MapManagedTypeToNative (StructureMemberInfo smi)
1487
1524
throw new InvalidOperationException ( $ "Field '{ smi . Info . Name } ' of structure '{ info . Name } ' should have a value of '{ expectedType } ' type, instead it had a '{ value . GetType ( ) } '") ;
1488
1525
}
1489
1526
1490
- if ( valueType == typeof ( string ) ) {
1491
- return context . Module . LookupRequiredVariableForString ( ( string ) value ) ;
1527
+ if ( valueType == typeof ( string ) || valueType == typeof ( StringHolder ) ) {
1528
+ var encoding = smi . Info . GetStringEncoding ( context . TypeCache ) ;
1529
+ return context . Module . LookupRequiredVariableForString ( StringHolder . AsHolder ( value , encoding ) ) ;
1492
1530
}
1493
1531
1494
1532
return value ;
@@ -1555,30 +1593,63 @@ public static string QuoteStringNoEscape (string s)
1555
1593
return $ "\" { s } \" ";
1556
1594
}
1557
1595
1558
- public static string QuoteString ( string value , bool nullTerminated = true )
1596
+ public static string QuoteString ( LlvmIrStringVariable variable , out ulong stringSize , bool nullTerminated = true )
1559
1597
{
1560
- return QuoteString ( value , out _ , nullTerminated ) ;
1561
- }
1598
+ if ( variable . Encoding == LlvmIrStringEncoding . UTF8 ) {
1599
+ var value = ( StringHolder ) variable . Value ;
1600
+ if ( value . Data == null ) {
1601
+ throw new InvalidOperationException ( "Internal error: null strings not supported here, they should be handled elsewhere." ) ;
1602
+ }
1562
1603
1563
- public static string QuoteString ( byte [ ] bytes )
1564
- {
1565
- return QuoteString ( bytes , bytes . Length , out _ , nullTerminated : false ) ;
1604
+ int byteCount = Encoding . UTF8 . GetByteCount ( value . Data ) ;
1605
+ var bytes = ArrayPool < byte > . Shared . Rent ( byteCount ) ;
1606
+
1607
+ try {
1608
+ Encoding . UTF8 . GetBytes ( value . Data , 0 , value . Data . Length , bytes , 0 ) ;
1609
+ return QuoteUtf8String ( bytes , byteCount , out stringSize , nullTerminated ) ;
1610
+ } finally {
1611
+ ArrayPool < byte > . Shared . Return ( bytes ) ;
1612
+ }
1613
+ }
1614
+
1615
+ if ( variable . Encoding == LlvmIrStringEncoding . Unicode ) {
1616
+ return QuoteUnicodeString ( variable , out stringSize , nullTerminated ) ;
1617
+ }
1618
+
1619
+ throw new InvalidOperationException ( $ "Internal error: unsupported string encoding { variable . Encoding } ") ;
1566
1620
}
1567
1621
1568
- public static string QuoteString ( string value , out ulong stringSize , bool nullTerminated = true )
1622
+ static string QuoteUnicodeString ( LlvmIrStringVariable variable , out ulong stringSize , bool nullTerminated = true )
1569
1623
{
1570
- var encoding = Encoding . UTF8 ;
1571
- int byteCount = encoding . GetByteCount ( value ) ;
1572
- var bytes = ArrayPool < byte > . Shared . Rent ( byteCount ) ;
1573
- try {
1574
- encoding . GetBytes ( value , 0 , value . Length , bytes , 0 ) ;
1575
- return QuoteString ( bytes , byteCount , out stringSize , nullTerminated ) ;
1576
- } finally {
1577
- ArrayPool < byte > . Shared . Return ( bytes ) ;
1624
+ var value = ( StringHolder ) variable . Value ;
1625
+ if ( value . Data == null ) {
1626
+ throw new InvalidOperationException ( "Internal error: null strings not supported here, they should be handled elsewhere." ) ;
1578
1627
}
1628
+
1629
+ // Each character/lexeme is encoded as iXY u0xVXYZ + comma and a space, and on top of that we have two square brackets and a trailing nul
1630
+ var sb = new StringBuilder ( ( value . Data . Length * 13 ) + 3 ) ; // rough estimate of capacity
1631
+ sb . Append ( '[' ) ;
1632
+ for ( int i = 0 ; i < value . Data . Length ; i ++ ) {
1633
+ var ch = ( short ) value . Data [ i ] ;
1634
+ if ( i > 0 ) {
1635
+ sb . Append ( ", " ) ;
1636
+ }
1637
+ sb . Append ( $ "{ variable . IrType } u0x{ ch : X2} ") ;
1638
+ }
1639
+
1640
+ if ( nullTerminated ) {
1641
+ if ( value . Data . Length > 0 ) {
1642
+ sb . Append ( ", " ) ;
1643
+ }
1644
+ sb . Append ( $ "{ variable . IrType } 0") ;
1645
+ }
1646
+ sb . Append ( ']' ) ;
1647
+
1648
+ stringSize = ( ulong ) value . Data . Length + ( nullTerminated ? 1u : 0u ) ;
1649
+ return sb . ToString ( ) ;
1579
1650
}
1580
1651
1581
- public static string QuoteString ( byte [ ] bytes , int byteCount , out ulong stringSize , bool nullTerminated = true )
1652
+ static string QuoteUtf8String ( byte [ ] bytes , int byteCount , out ulong stringSize , bool nullTerminated = true )
1582
1653
{
1583
1654
var sb = new StringBuilder ( byteCount * 2 ) ; // rough estimate of capacity
1584
1655
0 commit comments