Skip to content

Commit 17c439e

Browse files
authored
[<Struct>] DUs - eliminate dummy .ctor args, fix bug > 49 cases, simplify IL (#15695)
* Change construction methods for [<Struct>] unions to enable creating > 49 of cases, simplify IL
1 parent 4dd0341 commit 17c439e

File tree

4 files changed

+493
-409
lines changed

4 files changed

+493
-409
lines changed

src/Compiler/CodeGen/EraseUnions.fs

Lines changed: 95 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ let mkRuntimeTypeDiscriminateThen ilg avoidHelpers cuspec alt altName altTy afte
278278
let mkGetTagFromField ilg cuspec baseTy =
279279
mkNormalLdfld (refToFieldInTy baseTy (mkTagFieldId ilg cuspec))
280280

281+
let mkSetTagToField ilg cuspec baseTy =
282+
mkNormalStfld (refToFieldInTy baseTy (mkTagFieldId ilg cuspec))
283+
281284
let adjustFieldName hasHelpers nm =
282285
match hasHelpers, nm with
283286
| SpecialFSharpListHelpers, "Head" -> "HeadOrDefault"
@@ -334,29 +337,6 @@ let mkTagDiscriminate ilg cuspec _baseTy cidx =
334337
let mkTagDiscriminateThen ilg cuspec cidx after =
335338
[ mkGetTag ilg cuspec; mkLdcInt32 cidx ] @ mkCeqThen after
336339

337-
/// The compilation for struct unions relies on generating a set of constructors.
338-
/// If necessary some fake types are added to the constructor parameters to distinguish the signature.
339-
let rec extraTysAndInstrsForStructCtor (ilg: ILGlobals) cidx =
340-
match cidx with
341-
| 0 -> [ ilg.typ_Bool ], [ mkLdcInt32 0 ]
342-
| 1 -> [ ilg.typ_Byte ], [ mkLdcInt32 0 ]
343-
| 2 -> [ ilg.typ_SByte ], [ mkLdcInt32 0 ]
344-
| 3 -> [ ilg.typ_Char ], [ mkLdcInt32 0 ]
345-
| 4 -> [ ilg.typ_Int16 ], [ mkLdcInt32 0 ]
346-
| 5 -> [ ilg.typ_Int32 ], [ mkLdcInt32 0 ]
347-
| 6 -> [ ilg.typ_UInt16 ], [ mkLdcInt32 0 ]
348-
| _ ->
349-
let tys, instrs = extraTysAndInstrsForStructCtor ilg (cidx - 7)
350-
(ilg.typ_UInt32 :: tys, mkLdcInt32 0 :: instrs)
351-
352-
let takesExtraParams (alts: IlxUnionCase[]) =
353-
alts.Length > 1
354-
&& (alts |> Array.exists (fun d -> d.FieldDefs.Length > 0)
355-
||
356-
// Check if not all lengths are distinct
357-
alts |> Array.countBy (fun d -> d.FieldDefs.Length) |> Array.length
358-
<> alts.Length)
359-
360340
let convNewDataInstrInternal ilg cuspec cidx =
361341
let alt = altOfUnionSpec cuspec cidx
362342
let altTy = tyForAlt cuspec alt
@@ -379,27 +359,15 @@ let convNewDataInstrInternal ilg cuspec cidx =
379359

380360
instrs
381361
@ [ mkNormalNewobj (mkILCtorMethSpecForTy (baseTy, (ctorFieldTys @ tagfields))) ]
382-
elif cuspecRepr.RepresentAlternativeAsStructValue cuspec then
362+
elif
363+
cuspecRepr.RepresentAlternativeAsStructValue cuspec
364+
&& cuspecRepr.DiscriminationTechnique cuspec = IntegerTag
365+
then
366+
// Structs with fields should be created using maker methods (mkMakerName), only field-less cases are created this way
367+
assert (alt.IsNullary)
383368
let baseTy = baseTyOfUnionSpec cuspec
384-
385-
let instrs, tagfields =
386-
match cuspecRepr.DiscriminationTechnique cuspec with
387-
| IntegerTag -> [ mkLdcInt32 cidx ], [ mkTagFieldType ilg cuspec ]
388-
| _ -> [], []
389-
390-
let ctorFieldTys = alt.FieldTypes |> Array.toList
391-
392-
let extraTys, extraInstrs =
393-
if takesExtraParams cuspec.AlternativesArray then
394-
extraTysAndInstrsForStructCtor ilg cidx
395-
else
396-
[], []
397-
398-
instrs
399-
@ extraInstrs
400-
@ [
401-
mkNormalNewobj (mkILCtorMethSpecForTy (baseTy, (ctorFieldTys @ tagfields @ extraTys)))
402-
]
369+
let tagField = [ mkTagFieldType ilg cuspec ]
370+
[ mkLdcInt32 cidx; mkNormalNewobj (mkILCtorMethSpecForTy (baseTy, tagField)) ]
403371
else
404372
[ mkNormalNewobj (mkILCtorMethSpecForTy (altTy, Array.toList alt.FieldTypes)) ]
405373

@@ -414,6 +382,24 @@ let mkNewData ilg (cuspec, cidx) =
414382
let alt = altOfUnionSpec cuspec cidx
415383
let altName = alt.Name
416384
let baseTy = baseTyOfUnionSpec cuspec
385+
386+
let viaMakerCall () =
387+
[
388+
mkNormalCall (
389+
mkILNonGenericStaticMethSpecInTy (
390+
baseTy,
391+
mkMakerName cuspec altName,
392+
Array.toList alt.FieldTypes,
393+
constFormalFieldTy baseTy
394+
)
395+
)
396+
]
397+
398+
let viaGetAltNameProperty () =
399+
[
400+
mkNormalCall (mkILNonGenericStaticMethSpecInTy (baseTy, "get_" + altName, [], constFormalFieldTy baseTy))
401+
]
402+
417403
// If helpers exist, use them
418404
match cuspec.HasHelpers with
419405
| AllHelpers
@@ -422,30 +408,13 @@ let mkNewData ilg (cuspec, cidx) =
422408
if cuspecRepr.RepresentAlternativeAsNull(cuspec, alt) then
423409
[ AI_ldnull ]
424410
elif alt.IsNullary then
425-
[
426-
mkNormalCall (mkILNonGenericStaticMethSpecInTy (baseTy, "get_" + altName, [], constFormalFieldTy baseTy))
427-
]
411+
viaGetAltNameProperty ()
428412
else
429-
[
430-
mkNormalCall (
431-
mkILNonGenericStaticMethSpecInTy (
432-
baseTy,
433-
mkMakerName cuspec altName,
434-
Array.toList alt.FieldTypes,
435-
constFormalFieldTy baseTy
436-
)
437-
)
438-
]
413+
viaMakerCall ()
439414

440-
| NoHelpers ->
441-
if cuspecRepr.MaintainPossiblyUniqueConstantFieldForAlternative(cuspec, alt) then
442-
// This method is only available if not AllHelpers. It fetches the unique object for the alternative
443-
// without exposing direct access to the underlying field
444-
[
445-
mkNormalCall (mkILNonGenericStaticMethSpecInTy (baseTy, "get_" + altName, [], constFormalFieldTy baseTy))
446-
]
447-
else
448-
convNewDataInstrInternal ilg cuspec cidx
415+
| NoHelpers when (not alt.IsNullary) && cuspecRepr.RepresentAlternativeAsStructValue cuspec -> viaMakerCall ()
416+
| NoHelpers when cuspecRepr.MaintainPossiblyUniqueConstantFieldForAlternative(cuspec, alt) -> viaGetAltNameProperty ()
417+
| NoHelpers -> convNewDataInstrInternal ilg cuspec cidx
449418

450419
let mkIsData ilg (avoidHelpers, cuspec, cidx) =
451420
let alt = altOfUnionSpec cuspec cidx
@@ -916,13 +885,36 @@ let convAlternativeDef
916885
[ nullaryMeth ], [ nullaryProp ]
917886

918887
else
919-
let ilInstrs =
920-
[
921-
for i in 0 .. fields.Length - 1 do
922-
mkLdarg (uint16 i)
923-
yield! convNewDataInstrInternal g.ilg cuspec num
924-
]
925-
|> nonBranchingInstrsToCode
888+
let locals, ilInstrs =
889+
if repr.RepresentAlternativeAsStructValue info then
890+
let local = mkILLocal baseTy None
891+
let ldloca = I_ldloca(0us)
892+
893+
let ilInstrs =
894+
[
895+
ldloca
896+
ILInstr.I_initobj baseTy
897+
if (repr.DiscriminationTechnique info) = IntegerTag && num <> 0 then
898+
ldloca
899+
mkLdcInt32 num
900+
mkSetTagToField g.ilg cuspec baseTy
901+
for i in 0 .. fields.Length - 1 do
902+
ldloca
903+
mkLdarg (uint16 i)
904+
mkNormalStfld (mkILFieldSpecInTy (baseTy, fields[i].LowerName, fields[i].Type))
905+
mkLdloc 0us
906+
]
907+
908+
[ local ], ilInstrs
909+
else
910+
let ilInstrs =
911+
[
912+
for i in 0 .. fields.Length - 1 do
913+
mkLdarg (uint16 i)
914+
yield! convNewDataInstrInternal g.ilg cuspec num
915+
]
916+
917+
[], ilInstrs
926918

927919
let mdef =
928920
mkILNonGenericStaticMethod (
@@ -932,7 +924,7 @@ let convAlternativeDef
932924
|> Array.map (fun fd -> mkILParamNamed (fd.LowerName, fd.Type))
933925
|> Array.toList,
934926
mkILReturn baseTy,
935-
mkMethodBody (true, [], fields.Length, ilInstrs, attr, imports)
927+
mkMethodBody (true, locals, fields.Length + locals.Length, nonBranchingInstrsToCode ilInstrs, attr, imports)
936928
)
937929
|> addMethodGeneratedAttrs
938930
|> addAltAttribs
@@ -1219,9 +1211,20 @@ let mkClassUnionDef
12191211

12201212
let isStruct = td.IsStruct
12211213

1214+
let ctorAccess =
1215+
if cuspec.HasHelpers = AllHelpers then
1216+
ILMemberAccess.Assembly
1217+
else
1218+
cud.UnionCasesAccessibility
1219+
12221220
let selfFields, selfMeths, selfProps =
12231221

12241222
[
1223+
let minNullaryIdx =
1224+
cud.UnionCases
1225+
|> Array.tryFindIndex (fun t -> t.IsNullary)
1226+
|> Option.defaultValue -1
1227+
12251228
for cidx, alt in Array.indexed cud.UnionCases do
12261229
if
12271230
repr.RepresentAlternativeAsFreshInstancesOfRootClass(info, alt)
@@ -1238,31 +1241,25 @@ let mkClassUnionDef
12381241
| None -> Some g.ilg.typ_Object.TypeSpec
12391242
| Some ilTy -> Some ilTy.TypeSpec
12401243

1241-
let extraParamsForCtor =
1242-
if isStruct && takesExtraParams cud.UnionCases then
1243-
let extraTys, _extraInstrs = extraTysAndInstrsForStructCtor g.ilg cidx
1244-
List.map mkILParamAnon extraTys
1245-
else
1246-
[]
1247-
1248-
let ctorAccess =
1249-
(if cuspec.HasHelpers = AllHelpers then
1250-
ILMemberAccess.Assembly
1251-
else
1252-
cud.UnionCasesAccessibility)
1253-
12541244
let ctor =
1255-
(mkILSimpleStorageCtor (
1256-
baseInit,
1257-
baseTy,
1258-
extraParamsForCtor,
1259-
(fields @ tagFieldsInObject),
1260-
ctorAccess,
1261-
cud.DebugPoint,
1262-
cud.DebugImports
1263-
))
1264-
.With(customAttrs = mkILCustomAttrs [ GetDynamicDependencyAttribute g 0x660 baseTy ])
1265-
|> addMethodGeneratedAttrs
1245+
// Structs with fields are created using static makers methods
1246+
// Structs without fields can share constructor for the 'tag' value, we just create one
1247+
if isStruct && not (cidx = minNullaryIdx) then
1248+
[]
1249+
else
1250+
[
1251+
(mkILSimpleStorageCtor (
1252+
baseInit,
1253+
baseTy,
1254+
[],
1255+
(fields @ tagFieldsInObject),
1256+
ctorAccess,
1257+
cud.DebugPoint,
1258+
cud.DebugImports
1259+
))
1260+
.With(customAttrs = mkILCustomAttrs [ GetDynamicDependencyAttribute g 0x660 baseTy ])
1261+
|> addMethodGeneratedAttrs
1262+
]
12661263

12671264
let props, meths =
12681265
mkMethodsAndPropertiesForFields
@@ -1274,7 +1271,7 @@ let mkClassUnionDef
12741271
baseTy
12751272
alt.FieldDefs
12761273

1277-
yield (fields, ([ ctor ] @ meths), props)
1274+
yield (fields, (ctor @ meths), props)
12781275
]
12791276
|> List.unzip3
12801277
|> (fun (a, b, c) -> List.concat a, List.concat b, List.concat c)

0 commit comments

Comments
 (0)