Index: llvm/include/llvm/Target/GlobalISel/LegalizerInfo.td =================================================================== --- /dev/null +++ llvm/include/llvm/Target/GlobalISel/LegalizerInfo.td @@ -0,0 +1,342 @@ +//===- LegalizerInfo.td - Export legalizer rules ------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file reexport some rules from llvm/CodeGen/GlobalISel/LegalizerInfo.h +// +//===----------------------------------------------------------------------===// + +// Mutations +class LIMutation { + string Method = m; + string Arg = ""; + string Stmt = Method # Arg; +} + +class LIMChangeTo : LIMutation<"changeTo"> { + let Arg = "(" # i # ", " # t # ")"; +} + +class LIMChangeToIdx : LIMutation<"changeTo"> { + let Arg = "(" # typeIdx # ", " # fromTypeIdx # ")"; +} + +class LIMChangeElementSizeTo : LIMutation<"changeElementSizeTo"> { + let Arg = "(" # typeIdx # ", " # ty # ")"; +} + +class LIMChangeElementSizeToIdx : LIMutation<"changeElementSizeTo"> { + let Arg = "(" # typeIdx # ", " # fromTypeIdx # ")"; +} + + +class LIMWidenScalarOrEltToNextPow2 : LIMutation<"widenScalarOrEltToNextPow2"> { + let Arg = "(" # typeIdx # ", " # min # ")"; +} + +class LIMMoreElementsToNextPow2 : LIMutation<"moreElementsToNextPow2"> { + let Arg = "(" # typeIdx # ", " # min # ")"; +} + +class LIMScalarize : LIMutation<"scalarize"> { + let Arg = "(" # typeIdx # ")"; +} + + +def LIM_none : LIMutation<"">; // placeholder + +// Predicates +class LIPredicate { + string Method = m; + string Arg = ""; + string Stmt = Method # Arg; +} + +class LIPTypeIs : LIPredicate<"typeIs"> { + let Arg = "(" # i # ", " # ty # ")"; +} + +class LIPTypeInSet types> : LIPredicate<"typeInSet"> { + let Arg = "(" # i # ", " # !foldl("{", types, l, t, l#t#",") # "})"; +} + +class LIPTypeIsNot : LIPredicate<"typeIsNot"> { + let Arg = "(" # i # ", " # ty # ")"; +} + +class LIPIsScalar:LIPredicate<"isScalar"> { + let Arg = "("#n#")"; +} + +class LIPIsVector:LIPredicate<"isVector"> { + let Arg = "("#n#")"; +} + +class LIPIsPointer:LIPredicate<"isPointer"> { + let Arg = "("#n#")"; +} + +class LIPIsPointerInAddrSpace:LIPredicate<"isPointer"> { + let Arg = "(" # n # ", " # as #")"; +} + +class LIPElementTypeIs : LIPredicate<"elementTypeIs"> { + let Arg = "(" # i # ", " # ty # ")"; +} + +class LIPScalarNarrowerThan : LIPredicate<"scalarNarrowerThan"> { + let Arg = "(" # i # ", " # sz # ")"; +} + +class LIPScalarWiderThan : LIPredicate<"scalarWiderThan"> { + let Arg = "(" # i # ", " # sz # ")"; +} + +class LIPScalarOrEltNarrowerThan : LIPredicate<"scalarOrEltNarrowerThan"> { + let Arg = "(" # i # ", " # sz # ")"; +} + +class LIPScalarOrEltWiderThan : LIPredicate<"scalarOrEltWiderThan"> { + let Arg = "(" # i # ", " # sz # ")"; +} + +class LIPSizeNotPow2 : LIPredicate<"sizeNotPow2"> { + let Arg = "(" # i # ")"; +} + +class LIPScalarOrEltSizeNotPow2 : LIPredicate<"scalarOrEltSizeNotPow2"> { + let Arg = "(" # i # ")"; +} + +class LIPSizeIs : LIPredicate<"sizeIs"> { + let Arg = "(" # idx # ", " # sz # ")"; +} + +class LIPSameSize : LIPredicate<"sameSize"> { + let Arg = "(" # idx0 # ", " # idx1 # ")"; +} + +class LIPLargerThan : LIPredicate<"largerThan"> { + let Arg = "(" # idx0 # ", " # idx1 # ")"; +} + +class LIPSmallerThan : LIPredicate<"smallerThan"> { + let Arg = "(" # idx0 # ", " # idx1 # ")"; +} + +class LIPMemSizeInBytesNotPow2 : LIPredicate<"memSizeInBytesNotPow2"> { + let Arg = "(" # idx # ")"; +} + +class LIPNumElementsNotPow2 : LIPredicate<"numElementsNotPow2"> { + let Arg = "(" # idx # ")"; +} + +// Rules +class LIRule { + string Method = m; + string Arg = "()"; + string Stmt = Method # Arg; +} + +def LIAlwaysLegal:LIRule<"alwaysLegal">; +def LILower:LIRule<"lower">; +def LILibcall:LIRule<"libcall">; +def LIUnsupported:LIRule<"unsupported">; +def LICustom:LIRule<"custom">; +def LIFallback:LIRule<"fallback">; +def LIUnsupportedIfMemSizeNotPow2:LIRule<"unsupportedIfMemSizeNotPow2">; +def LILowerIfMemSizeNotPow2:LIRule<"lowerIfMemSizeNotPow2">; + +class LILegalIf : LIRule<"legalIf"> { + let Arg = "(" # p.Stmt # ")"; +} + +class LILegalFor types> : LIRule<"legalFor"> { + let Arg = !foldl("({", types, l, t, l#t#",")#"})"; +} + +class LILegalForTypeWithAnyImm types> : LIRule<"legalForTypeWithAnyImm"> { + let Arg = !foldl("({", types, l, t, l#t#",")#"})"; +} + +class LILegalForCartesianProduct types0, + list types1 = [], + list types2 = []> : LIRule<"legalForCartesianProduct"> { + let Arg = !foldl("({", types0, l, t, l#t#",")#"}" # + !cond(!not(!empty(types1)) : !foldl(", {", types1, l, t, l#t#",")#"}", 1:"") # + !cond(!not(!empty(types2)) : !foldl(", {", types2, l, t, l#t#","#"}"), 1:"") # ")"; +} + +class LIBitcastIf : LIRule<"bitcastIf"> { + let Arg = "(" # p.Stmt # ", "#m.Stmt#")"; +} + +class LILowerIf : LIRule<"lowerIf"> { + let Arg = "(" # p.Stmt # !cond(!ne(m, LIM_none) : ", " # m.Stmt, 1 : "") # ")"; +} + +class LILowerFor types, LIMutation m = LIM_none> : LIRule<"lowerFor"> { + let Arg = "(" # !foldl("{", types, l, t, l # t # ",") # "}" # + !cond(!ne(m, LIM_none) : ", " # m.Stmt, 1 : "") # ")"; +} + +class LILowerForCartesianProduct types0, + list types1 = [], + list types2 = []> : LIRule<"lowerForCartesianProduct"> { + let Arg = !foldl("({", types0, l, t, l#t#",")#"}" # + !cond(!not(!empty(types1)) : !foldl(", {", types1, l, t, l#t#",")#"}", 1:"") # + !cond(!not(!empty(types2)) : !foldl(", {", types2, l, t, l # t # "," # "}"), 1:"") # ")"; +} + +class LILibcallIf : LIRule<"libcallIf"> { + let Arg = "(" # p.Stmt # ")"; +} + +class LILibcallFor types> : LIRule<"libcallFor"> { + let Arg = !foldl("({", types, l, t, l # t # ",") # "})"; +} + +class LILibcallForCartesianProduct types0, + list types1 = []> : LIRule<"libcallForCartesianProduct"> { + let Arg = !foldl("({", types0, l, t, l#t#",")#"}" # + !cond(!not(!empty(types1)) : !foldl(", {", types1, l, t, l # t # "," # "}"), 1:"") # ")"; +} + +class LIWidenScalarIf : LIRule<"widenScalarIf"> { + let Arg = "(" # p.Stmt # ", " # m.Stmt # ")"; +} + +class LINarrowScalarIf : LIRule<"narrowScalarIf"> { + let Arg = "(" # p.Stmt # ", " # m.Stmt # ")"; +} + +class LINarrowScalarFor : LIRule<"narrowScalarFor"> { + let Arg = "(" # p.Stmt # ", " # m.Stmt # ")"; +} + +class LIMoreElementsIf : LIRule<"moreElementsIf"> { + let Arg = "(" # p.Stmt # ", " # m.Stmt # ")"; +} + +class LIFewerElementsIf : LIRule<"fewerElementsIf"> { + let Arg = "(" # p.Stmt # ", " # m.Stmt # ")"; +} + +class LIUnsupportedIf : LIRule<"unsupportedIf"> { + let Arg = "(" # p.Stmt # ")"; +} + +class LIUnsupportedFor types> : LIRule<"UnsupportedFor"> { + let Arg = !foldl("({", types, l, t, l#t#",")#"})"; +} + +class LICustomIf : LIRule<"libcallIf"> { + let Arg = "(" # p.Stmt # ")"; +} + +class LICustomFor types> : LIRule<"libcallFor"> { + let Arg = !foldl("({", types, l, t, l#t#",")#"})"; +} + +class LICustomForCartesianProduct types0, + list types1 = []> : LIRule<"customForCartesianProduct"> { + let Arg = !foldl("({", types0, l, t, l#t#",")#"}" # + !cond(!not(!empty(types1)) : !foldl(", {", types1, l, t, l # t # "," # "}"), 1:"") # ")"; +} + +class LIWidenScalarToNextPow2 : LIRule <"widenScalarToNextPow2"> { + let Arg = "(" # ", " # minSize # ")"; +} + +class LIWidenScalarOrEltToNextPow2 : LIRule <"widenScalarOrEltToNextPow2"> { + let Arg = "(" # ", " # minSize # ")"; +} + +class LINarrowScalar : LIRule<"narrowScalar"> { + let Arg = "(" # i # ", " # m.Stmt # ")"; +} + +class LIScalarize : LIRule<"scalarize"> { + let Arg = "(" # i # ")"; +} + +class LIScalarizeIf : LIRule<"scalarize"> { + let Arg = "(" # p.Stmt # ", " # i # ")"; +} + +class LIMinScalarOrElt : LIRule<"minScalarOrElt"> { + let Arg = "(" # i # ", " # t # ")"; +} + +class LIMinScalarOrEltIf : LIRule<"minScalarOrEltIf"> { + let Arg = "(" # p.Stmt # ", " # i # ", " # t # ")"; +} + +class LIMinScalar : LIRule<"minScalar"> { + let Arg = "(" # i # ", " # t # ")"; +} + +class LIMaxScalarOrElt : LIRule<"maxScalarOrElt"> { + let Arg = "(" # i # ", " # t # ")"; +} + +class LIMaxScalar : LIRule<"maxScalar"> { + let Arg = "(" # i # ", " # t # ")"; +} + +class LIMaxScalarIf : LIRule<"maxScalarIf"> { + let Arg = "(" # p.Stmt # ", " # i # ", " # t # ")"; +} + +class LIClampScalar : LIRule<"clampScalar"> { + let Arg = "(" # i # ", " # min # ", " # max # ")"; +} + +class LIClampScalarOrElt : LIRule<"clampScalarOrElt"> { + let Arg = "(" # i # ", " # min # ", " # max # ")"; +} + +class LIMinScalarSameAs : LIRule<"minScalarSameAs"> { + let Arg = "(" # typeIdx # ", " # largeTypeIdx # ")"; +} + +class LIMaxScalarSameAs : LIRule<"maxScalarSameAs"> { + let Arg = "(" # typeIdx # ", " # narrowTypeIdx # ")"; +} + +class LIScalarSameSizeAs : LIRule<"scalarSameSizeAs"> { + let Arg = "(" # typeIdx # ", " # sameSizeIdx # ")"; +} + +class LIMinScalarEltSameAsIf : LIRule<"minScalarEltSameAsIf"> { + let Arg = "(" # p.Stmt # ", " # typeIdx # ", " # largeTypeIdx; +} + +class LIMoreElementsToNextPow2 : LIRule<"moreElementsToNextPow2"> { + let Arg = "(" # typeIdx # ")"; +} + +class LIClampMinNumElements :LIRule<"clampMinNumElements"> { + let Arg = "(" # typeIdx # ", " # eltTy # ", " # minElements # ")"; +} + +class LIClampMaxNumElements :LIRule<"clampMaxNumElements"> { + let Arg = "(" # typeIdx # ", " # eltTy # ", " # maxElements # ")"; +} + +class LIClampNumElements :LIRule<"clampNumElements"> { + let Arg = "(" # typeIdx # ", " # minTy # ", " # maxTy # ")"; +} + +// final definition +class LIActionDefinition ops, list rules> { + list Predicates = []; + list Ops = ops; + list Rules = rules; +} + Index: llvm/include/llvm/Target/GlobalISel/Target.td =================================================================== --- llvm/include/llvm/Target/GlobalISel/Target.td +++ llvm/include/llvm/Target/GlobalISel/Target.td @@ -20,8 +20,25 @@ // GlobalISel matcher. class LLT; -def s32 : LLT; -def s64 : LLT; +class Pointer : LLT { + int AddressSpace = as; +} + +class Scalar : LLT { + int ScalarSizeInBits = sz; +} + +class Vector : LLT { + int NumElements = n; + int ScalarSizeInBits = sz; +} +class LLTVector : LLT { + int NumElements = n; + LLT Ty = t; +} + +def s32 : Scalar<32>; +def s64 : Scalar<64>; // Defines a matcher for complex operands. This is analogous to ComplexPattern // from SelectionDAG. Index: llvm/include/llvm/Target/Target.td =================================================================== --- llvm/include/llvm/Target/Target.td +++ llvm/include/llvm/Target/Target.td @@ -1683,6 +1683,11 @@ // include "llvm/Target/GlobalISel/SelectionDAGCompat.td" +//===----------------------------------------------------------------------===// +// Pull in the common legalizer info generation. +// +include "llvm/Target/GlobalISel/LegalizerInfo.td" + //===----------------------------------------------------------------------===// // Pull in the common support for Pfm Counters generation. // Index: llvm/test/TableGen/LegalizerInfoEmitter.td =================================================================== --- /dev/null +++ llvm/test/TableGen/LegalizerInfoEmitter.td @@ -0,0 +1,52 @@ +// RUN: llvm-tblgen -gen-legalizer-info -I %p/../../include %s | FileCheck %s + +include "llvm/Target/Target.td" + +def MyTarget : Target; + +def FeatureFoo : SubtargetFeature<"foo", "HasFoo", "true", "">; + +def p0 : Pointer<0>; +def s1 : Scalar<1>; +def s8 : Scalar<8>; +def s16 : Scalar<16>; +def s128 : Scalar<128>; + +// CHECK: getActionDefinitionsBuilder({G_IMPLICIT_DEF,}) +// CHECK-NEXT: .legalFor({p0,s1,s8,s16,s32,}) +def : LIActionDefinition<[ + G_IMPLICIT_DEF +], [LILegalFor<[p0, s1, s8, s16, s32]>]>; + +// CHECK: if (Subtarget.hasFeature(MyTarget::FeatureFoo) && true) +// CHECK: getActionDefinitionsBuilder({G_LOAD,G_STORE,}) +// CHECK-NEXT: .legalFor({s8,s16,s32,p0,}) +// CHECK-NEXT: .legalIf(typeIs(1, s1)) +let Predicates = [FeatureFoo] in +def : LIActionDefinition<[ + G_LOAD, G_STORE +], [ + LILegalFor<[s8, s16, s32, p0]>, + LILegalIf>] +>; + +// CHECK: getActionDefinitionsBuilder({G_FREEZE,}) +// CHECK-NEXT: .legalFor({s8,s16,s32,p0,}) +// CHECK-NEXT: .clampScalar(0, s1, s64) +// CHECK-NEXT: .fewerElementsIf( +// CHECK-NEXT: [=](const LegalityQuery &Query) { +// CHECK: changeTo(0, s1)) +def : LIActionDefinition<[ + G_FREEZE +], [ + LILegalFor<[s8, s16, s32, p0]>, + LIClampScalar<0, s1, s64>, + LIFewerElementsIf, LIMChangeTo<0, s1>> + ] +>; Index: llvm/utils/TableGen/CMakeLists.txt =================================================================== --- llvm/utils/TableGen/CMakeLists.txt +++ llvm/utils/TableGen/CMakeLists.txt @@ -34,6 +34,7 @@ InstrInfoEmitter.cpp InstrDocsEmitter.cpp IntrinsicEmitter.cpp + LegalizerInfoEmitter.cpp OptEmitter.cpp OptParserEmitter.cpp OptRSTEmitter.cpp Index: llvm/utils/TableGen/LegalizerInfoEmitter.cpp =================================================================== --- /dev/null +++ llvm/utils/TableGen/LegalizerInfoEmitter.cpp @@ -0,0 +1,182 @@ +//===- LegalizerInfoEmitter.cpp - generate LegalizerInfo -*--------C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This Tablegen backend emits LegalizerInfo +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include +#include +#include + +#include "CodeGenHwModes.h" +#include "CodeGenRegisters.h" +#include "CodeGenTarget.h" + +#define DEBUG_TYPE "legalizer-info-emitter" + +using namespace llvm; + +namespace { + +// Any helper data structures can be defined here. Some backends use +// structs to collect information from the records. + +class LegalizerInfoEmitter { +private: + RecordKeeper &Records; + CodeGenTarget Target; + + void generateLLTs(raw_ostream &OS); + void generateActions(raw_ostream &OS); + void generateRules(raw_ostream &OS, const std::vector &Rules); + +public: + LegalizerInfoEmitter(RecordKeeper &RK) : Records(RK), Target(RK) {} + void emitHeader(raw_ostream &OS); + void emitImpl(raw_ostream &OS); + + void run(raw_ostream &OS); +}; // emitter class + +void LegalizerInfoEmitter::generateLLTs(raw_ostream &OS) { + auto LLTs = Records.getAllDerivedDefinitions("LLT"); + for (const auto &T : LLTs) { + OS << " const LLT " << T->getName() << " = LLT::"; + if (T->isSubClassOf("Pointer")) { + OS << "pointer(" << T->getValueAsInt("AddressSpace") + << ", TM.getPointerSizeInBits(" << T->getValueAsInt("AddressSpace") + << "));\n"; + } + if (T->isSubClassOf("Scalar")) { + OS << "scalar(" << T->getValueAsInt("ScalarSizeInBits") << ");\n"; + } + if (T->isSubClassOf("Vector")) { + OS << "vector(" << T->getValueAsInt("NumElements") << ", " + << T->getValueAsInt("ScalarSizeInBits") << ");\n"; + } + if (T->isSubClassOf("LLTVector")) { + OS << "vector(" << T->getValueAsInt("NumElements") << ", " + << T->getValueAsDef("Ty")->getName() << ");\n"; + } + } + + OS << "\n"; +} + +void LegalizerInfoEmitter::generateActions(raw_ostream &OS) { + OS << " using namespace TargetOpcode;\n" + << " using namespace LegalityPredicates;\n" + << " using namespace LegalizeMutations;\n" + << "\n"; + auto Defs = Records.getAllDerivedDefinitions("LIActionDefinition"); + for (const auto &Def : Defs) { + auto Predicates = Def->getValueAsListOfDefs("Predicates"); + OS << " if ("; + for (const auto &Predicate : Predicates) { + OS << "Subtarget.hasFeature(" << Target.getName() + << "::" << Predicate->getName() << ") && "; + } + // begin predicate + OS << "true) {\n"; + + auto Ops = Def->getValueAsListInit("Ops"); + OS << " getActionDefinitionsBuilder({"; + for (const auto &Op : Ops->getValues()) { + OS << Op->getAsString() << ","; + } + OS << "})\n"; + + auto Rules = Def->getValueAsListOfDefs("Rules"); + generateRules(OS, Rules); + OS << " ;\n\n"; + // end predicate + OS << " } // end if\n"; + } +} + +void LegalizerInfoEmitter::generateRules(raw_ostream &OS, + const std::vector &Rules) { + for (const auto &Rule : Rules) { + OS << " ." << Rule->getValueAsString("Stmt") << "\n"; + } +} + +void LegalizerInfoEmitter::emitHeader(raw_ostream &OS) { + OS << "#include \"llvm/CodeGen/GlobalISel/LegalizerInfo.h\"\n\n"; + + OS << "namespace llvm {\n\n"; + OS << "class " << Target.getName() << "Subtarget;\n" + << "class " << Target.getName() << "TargetMachine;\n\n"; + + OS << "class " << Target.getName() + << "GenLegalizerInfo : public LegalizerInfo {\n" + << "protected:\n" + << " const " << Target.getName() << "Subtarget &Subtarget;" + << "public:\n" + << " " << Target.getName() << "GenLegalizerInfo(const " + << Target.getName() << "Subtarget &STI" + << ");\n"; + + OS << "}; // GenLegalizerInfo\n\n"; + + OS << "} // namespace llvm\n\n"; +} + +void LegalizerInfoEmitter::emitImpl(raw_ostream &OS) { + auto LLTs = Records.getAllDerivedDefinitions("LLT"); + OS << "namespace llvm {\n\n"; + OS << Target.getName() << "GenLegalizerInfo::" << Target.getName() + << "GenLegalizerInfo(const " << Target.getName() << "Subtarget &STI" + << ") : Subtarget(STI)" + << " {\n" + << " const auto &TM = " + "Subtarget.getTargetLowering()->getTargetMachine();\n\n"; + generateLLTs(OS); + generateActions(OS); + + OS << "}\n\n"; + OS << "} // namespace llvm\n\n"; +} + +} // anonymous namespace + +void LegalizerInfoEmitter::run(raw_ostream &OS) { + emitSourceFileHeader("Generated Legalizer Info", OS); + OS << "#ifdef GET_LEGALIZERINFO_HEADER\n" + << "#undef GET_LEGALIZERINFO_HEADER\n"; + emitHeader(OS); + OS << "#endif // GET_LEGALIZERINFO_HEADER\n\n"; + + OS << "#ifdef GET_LEGALIZERINFO_IMPL\n" + << "#undef GET_LEGALIZERINFO_IMPL\n\n"; + + OS << "#include \"llvm/CodeGen/GlobalISel/LegalizerHelper.h\"\n" + << "#include \"llvm/CodeGen/TargetOpcodes.h\"\n" + << "#include \"llvm/CodeGen/ValueTypes.h\"\n\n"; + emitImpl(OS); + OS << "#endif // GET_LEGALIZERINFO_IMPL\n"; +} + +namespace llvm { + +void EmitLegalizerInfo(RecordKeeper &RK, raw_ostream &OS) { + LegalizerInfoEmitter(RK).run(OS); +} + +} // namespace llvm Index: llvm/utils/TableGen/TableGen.cpp =================================================================== --- llvm/utils/TableGen/TableGen.cpp +++ llvm/utils/TableGen/TableGen.cpp @@ -49,6 +49,7 @@ GenSearchableTables, GenGlobalISel, GenGICombiner, + GenLegalizerInfo, GenX86EVEX2VEXTables, GenX86FoldTables, GenRegisterBank, @@ -124,6 +125,7 @@ "Generate GlobalISel selector"), clEnumValN(GenGICombiner, "gen-global-isel-combiner", "Generate GlobalISel combiner"), + clEnumValN(GenLegalizerInfo, "gen-legalizer-info", "Generate LegalizerInfo"), clEnumValN(GenX86EVEX2VEXTables, "gen-x86-EVEX2VEX-tables", "Generate X86 EVEX to VEX compress tables"), clEnumValN(GenX86FoldTables, "gen-x86-fold-tables", @@ -209,6 +211,9 @@ case GenOptParserDefs: EmitOptParser(Records, OS); break; + case GenLegalizerInfo: + EmitLegalizerInfo(Records, OS); + break; case GenOptRST: EmitOptRST(Records, OS); break;