From 1de99829b6bebe3310682efac8be2a9a95323220 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Sat, 4 Jun 2011 04:11:37 +0000 Subject: [PATCH] Teach TableGen to evaluate DAG expressions as set operations. A TableGen backend can define how certain classes can be expanded into ordered sets of defs, typically by evaluating a specific field in the record. The SetTheory class can then evaluate DAG expressions that refer to these named sets. A number of standard set and list operations are predefined, and the backend can add more specialized operators if needed. The -print-sets backend is used by SetTheory.td to provide examples. This is intended to simplify how register classes are defined: def GR32_NOSP : RegisterClass<"X86", [i32], 32, (sub GR32, ESP)>; git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@132621 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/TableGen/SetTheory.td | 167 +++++++++++++++++++++ utils/TableGen/CMakeLists.txt | 1 + utils/TableGen/SetTheory.cpp | 272 ++++++++++++++++++++++++++++++++++ utils/TableGen/SetTheory.h | 133 +++++++++++++++++ utils/TableGen/TableGen.cpp | 21 ++- 5 files changed, 593 insertions(+), 1 deletion(-) create mode 100644 test/TableGen/SetTheory.td create mode 100644 utils/TableGen/SetTheory.cpp create mode 100644 utils/TableGen/SetTheory.h diff --git a/test/TableGen/SetTheory.td b/test/TableGen/SetTheory.td new file mode 100644 index 00000000000..e0abc631e0d --- /dev/null +++ b/test/TableGen/SetTheory.td @@ -0,0 +1,167 @@ +// Test evaluation of set operations in dags. +// RUN: tblgen -print-sets %s | FileCheck %s +// XFAIL: vg_leak +// +// The -print-sets driver configures a primitive SetTheory instance that +// understands these sets: + +class Set { + dag Elements = d; +} + +// It prints all Set instances and their ordered set interpretation. + +// Define some elements. +def a; +def b; +def c; +def d; + +// The 'add' operator evaluates and concatenates its arguments. +def add; +def S0a : Set<(add)>; +def S0b : Set<(add a)>; +def S0c : Set<(add a, b)>; +def S0d : Set<(add b, a)>; +def S0e : Set<(add a, a)>; +def S0f : Set<(add a, a, b, a, c, b, d, a)>; +def S0g : Set<(add b, a, b)>; +// CHECK: S0a = [ ] +// CHECK: S0b = [ a ] +// CHECK: S0c = [ a b ] +// CHECK: S0d = [ b a ] +// CHECK: S0e = [ a ] +// CHECK: S0f = [ a b c d ] +// CHECK: S0g = [ b a ] + +// Defs of Set class expand into their elements. +// Mixed sets and elements are flattened. +def S1a : Set<(add S0a)>; +def S1b : Set<(add S0a, S0a)>; +def S1c : Set<(add S0d, S0f)>; +def S1d : Set<(add d, S0d, S0f)>; +// CHECK: S1a = [ ] +// CHECK: S1b = [ ] +// CHECK: S1c = [ b a c d ] +// CHECK: S1d = [ d b a c ] + +// The 'sub' operator returns the first argument with the following arguments +// removed. +def sub; +def S2a : Set<(sub S1a, S1c)>; +def S2b : Set<(sub S1c, S1d)>; +def S2c : Set<(sub S1c, b)>; +def S2d : Set<(sub S1c, S0c)>; +def S2e : Set<(sub S1c, S2d)>; +// CHECK: S2a = [ ] +// CHECK: S2b = [ ] +// CHECK: S2c = [ a c d ] +// CHECK: S2d = [ c d ] +// CHECK: S2e = [ b a ] + +// The 'and' operator intersects two sets. The result has the same order as the +// first argument. +def and; +def S3a : Set<(and S2d, S2e)>; +def S3b : Set<(and S2d, S1d)>; +// CHECK: S3a = [ ] +// CHECK: S3b = [ c d ] + +// The 'shl' operator removes the first N elements. +def shl; +def S4a : Set<(shl S0f, 0)>; +def S4b : Set<(shl S0f, 1)>; +def S4c : Set<(shl S0f, 3)>; +def S4d : Set<(shl S0f, 4)>; +def S4e : Set<(shl S0f, 5)>; +// CHECK: S4a = [ a b c d ] +// CHECK: S4b = [ b c d ] +// CHECK: S4c = [ d ] +// CHECK: S4d = [ ] +// CHECK: S4e = [ ] + +// The 'trunc' operator truncates after the first N elements. +def trunc; +def S5a : Set<(trunc S0f, 0)>; +def S5b : Set<(trunc S0f, 1)>; +def S5c : Set<(trunc S0f, 3)>; +def S5d : Set<(trunc S0f, 4)>; +def S5e : Set<(trunc S0f, 5)>; +// CHECK: S5a = [ ] +// CHECK: S5b = [ a ] +// CHECK: S5c = [ a b c ] +// CHECK: S5d = [ a b c d ] +// CHECK: S5e = [ a b c d ] + +// The 'rotl' operator rotates left, but also accepts a negative shift. +def rotl; +def S6a : Set<(rotl S0f, 0)>; +def S6b : Set<(rotl S0f, 1)>; +def S6c : Set<(rotl S0f, 3)>; +def S6d : Set<(rotl S0f, 4)>; +def S6e : Set<(rotl S0f, 5)>; +def S6f : Set<(rotl S0f, -1)>; +def S6g : Set<(rotl S0f, -4)>; +def S6h : Set<(rotl S0f, -5)>; +// CHECK: S6a = [ a b c d ] +// CHECK: S6b = [ b c d a ] +// CHECK: S6c = [ d a b c ] +// CHECK: S6d = [ a b c d ] +// CHECK: S6e = [ b c d a ] +// CHECK: S6f = [ d a b c ] +// CHECK: S6g = [ a b c d ] +// CHECK: S6h = [ d a b c ] + +// The 'rotr' operator rotates right, but also accepts a negative shift. +def rotr; +def S7a : Set<(rotr S0f, 0)>; +def S7b : Set<(rotr S0f, 1)>; +def S7c : Set<(rotr S0f, 3)>; +def S7d : Set<(rotr S0f, 4)>; +def S7e : Set<(rotr S0f, 5)>; +def S7f : Set<(rotr S0f, -1)>; +def S7g : Set<(rotr S0f, -4)>; +def S7h : Set<(rotr S0f, -5)>; +// CHECK: S7a = [ a b c d ] +// CHECK: S7b = [ d a b c ] +// CHECK: S7c = [ b c d a ] +// CHECK: S7d = [ a b c d ] +// CHECK: S7e = [ d a b c ] +// CHECK: S7f = [ b c d a ] +// CHECK: S7g = [ a b c d ] +// CHECK: S7h = [ b c d a ] + +// The 'decimate' operator picks every N'th element. +def decimate; +def e0; +def e1; +def e2; +def e3; +def e4; +def e5; +def e6; +def e7; +def e8; +def e9; +def E : Set<(add e0, e1, e2, e3, e4, e5, e6, e7, e8, e9)>; +def S8a : Set<(decimate E, 3)>; +def S8b : Set<(decimate E, 9)>; +def S8c : Set<(decimate E, 10)>; +def S8d : Set<(decimate (rotl E, 1), 2)>; +def S8e : Set<(add (decimate E, 2), (decimate (rotl E, 1), 2))>; +// CHECK: S8a = [ e0 e3 e6 e9 ] +// CHECK: S8b = [ e0 e9 ] +// CHECK: S8c = [ e0 ] +// CHECK: S8d = [ e1 e3 e5 e7 e9 ] +// CHECK: S8e = [ e0 e2 e4 e6 e8 e1 e3 e5 e7 e9 ] + +// The 'sequence' operator finds a sequence of records from their name. +def sequence; +def S9a : Set<(sequence "e%u", 3, 7)>; +def S9b : Set<(sequence "e%u", 7, 3)>; +def S9c : Set<(sequence "e%u", 0, 0)>; +def S9d : Set<(sequence "S%ua", 7, 9)>; +// CHECK: S9a = [ e3 e4 e5 e6 e7 ] +// CHECK: S9b = [ e7 e6 e5 e4 e3 ] +// CHECK: S9c = [ e0 ] +// CHECK: S9d = [ a b c d e0 e3 e6 e9 e4 e5 e7 ] diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index 514b1912992..db5823729a1 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -34,6 +34,7 @@ add_llvm_utility(tblgen OptParserEmitter.cpp Record.cpp RegisterInfoEmitter.cpp + SetTheory.cpp StringMatcher.cpp SubtargetEmitter.cpp TGLexer.cpp diff --git a/utils/TableGen/SetTheory.cpp b/utils/TableGen/SetTheory.cpp new file mode 100644 index 00000000000..d2f1b22a10e --- /dev/null +++ b/utils/TableGen/SetTheory.cpp @@ -0,0 +1,272 @@ +//===- SetTheory.cpp - Generate ordered sets from DAG expressions ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the SetTheory class that computes ordered sets of +// Records from DAG expressions. +// +//===----------------------------------------------------------------------===// + +#include "SetTheory.h" +#include "Record.h" +#include "llvm/Support/Format.h" + +using namespace llvm; + +// Define the standard operators. +namespace { + +typedef SetTheory::RecSet RecSet; +typedef SetTheory::RecVec RecVec; + +// (add a, b, ...) Evaluate and union all arguments. +struct AddOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts) { + ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts); + } +}; + +// (sub Add, Sub, ...) Set difference. +struct SubOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts) { + if (Expr->arg_size() < 2) + throw "Set difference needs at least two arguments: " + + Expr->getAsString(); + RecSet Add, Sub; + ST.evaluate(*Expr->arg_begin(), Add); + ST.evaluate(Expr->arg_begin() + 1, Expr->arg_end(), Sub); + for (RecSet::iterator I = Add.begin(), E = Add.end(); I != E; ++I) + if (!Sub.count(*I)) + Elts.insert(*I); + } +}; + +// (and S1, S2) Set intersection. +struct AndOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts) { + if (Expr->arg_size() != 2) + throw "Set intersection requires two arguments: " + Expr->getAsString(); + RecSet S1, S2; + ST.evaluate(Expr->arg_begin()[0], S1); + ST.evaluate(Expr->arg_begin()[1], S2); + for (RecSet::iterator I = S1.begin(), E = S1.end(); I != E; ++I) + if (S2.count(*I)) + Elts.insert(*I); + } +}; + +// SetIntBinOp - Abstract base class for (Op S, N) operators. +struct SetIntBinOp : public SetTheory::Operator { + virtual void apply(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts) =0; + + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts) { + if (Expr->arg_size() != 2) + throw "Operator requires (Op Set, Int) arguments: " + Expr->getAsString(); + RecSet Set; + ST.evaluate(Expr->arg_begin()[0], Set); + IntInit *II = dynamic_cast(Expr->arg_begin()[1]); + if (!II) + throw "Second argument must be an integer: " + Expr->getAsString(); + apply(ST, Expr, Set, II->getValue(), Elts); + } +}; + +// (shl S, N) Shift left, remove the first N elements. +struct ShlOp : public SetIntBinOp { + void apply(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts) { + if (N < 0) + throw "Positive shift required: " + Expr->getAsString(); + if (unsigned(N) < Set.size()) + Elts.insert(Set.begin() + N, Set.end()); + } +}; + +// (trunc S, N) Truncate after the first N elements. +struct TruncOp : public SetIntBinOp { + void apply(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts) { + if (N < 0) + throw "Positive length required: " + Expr->getAsString(); + if (unsigned(N) > Set.size()) + N = Set.size(); + Elts.insert(Set.begin(), Set.begin() + N); + } +}; + +// Left/right rotation. +struct RotOp : public SetIntBinOp { + const bool Reverse; + + RotOp(bool Rev) : Reverse(Rev) {} + + void apply(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts) { + if (Reverse) + N = -N; + // N > 0 -> rotate left, N < 0 -> rotate right. + if (Set.empty()) + return; + if (N < 0) + N = Set.size() - (-N % Set.size()); + else + N %= Set.size(); + Elts.insert(Set.begin() + N, Set.end()); + Elts.insert(Set.begin(), Set.begin() + N); + } +}; + +// (decimate S, N) Pick every N'th element of S. +struct DecimateOp : public SetIntBinOp { + void apply(SetTheory &ST, DagInit *Expr, + RecSet &Set, int64_t N, + RecSet &Elts) { + if (N <= 0) + throw "Positive stride required: " + Expr->getAsString(); + for (unsigned I = 0; I < Set.size(); I += N) + Elts.insert(Set[I]); + } +}; + +// (sequence "Format", From, To) Generate a sequence of records by name. +struct SequenceOp : public SetTheory::Operator { + RecordKeeper &Records; + + SequenceOp(RecordKeeper&R) : Records(R) {} + + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts) { + if (Expr->arg_size() != 3) + throw "Bad args to (sequence \"Format\", From, To): " + + Expr->getAsString(); + std::string Format; + if (StringInit *SI = dynamic_cast(Expr->arg_begin()[0])) + Format = SI->getValue(); + else + throw "Format must be a string: " + Expr->getAsString(); + + int64_t From, To; + if (IntInit *II = dynamic_cast(Expr->arg_begin()[1])) + From = II->getValue(); + else + throw "From must be an integer: " + Expr->getAsString(); + if (IntInit *II = dynamic_cast(Expr->arg_begin()[2])) + To = II->getValue(); + else + throw "From must be an integer: " + Expr->getAsString(); + + int Step = From <= To ? 1 : -1; + for (To += Step; From != To; From += Step) { + std::string Name; + raw_string_ostream OS(Name); + OS << format(Format.c_str(), From); + Record *Rec = Records.getDef(OS.str()); + if (!Rec) + throw "No def named '" + Name + "': " + Expr->getAsString(); + // Try to reevaluate Rec in case it is a set. + if (const RecVec *Result = ST.expand(Rec)) + Elts.insert(Result->begin(), Result->end()); + else + Elts.insert(Rec); + } + } +}; + +// Expand a Def into a set by evaluating one of its fields. +struct FieldExpander : public SetTheory::Expander { + StringRef FieldName; + + FieldExpander(StringRef fn) : FieldName(fn) {} + + void expand(SetTheory &ST, Record *Def, RecSet &Elts) { + ST.evaluate(Def->getValueInit(FieldName), Elts); + } +}; +} // end anonymous namespace + +SetTheory::SetTheory(RecordKeeper *Records) { + addOperator("add", new AddOp); + addOperator("sub", new SubOp); + addOperator("and", new AndOp); + addOperator("shl", new ShlOp); + addOperator("trunc", new TruncOp); + addOperator("rotl", new RotOp(false)); + addOperator("rotr", new RotOp(true)); + addOperator("decimate", new DecimateOp); + if (Records) + addOperator("sequence", new SequenceOp(*Records)); +} + +void SetTheory::addOperator(StringRef Name, Operator *Op) { + Operators[Name] = Op; +} + +void SetTheory::addExpander(StringRef ClassName, Expander *E) { + Expanders[ClassName] = E; +} + +void SetTheory::addFieldExpander(StringRef ClassName, StringRef FieldName) { + addExpander(ClassName, new FieldExpander(FieldName)); +} + +void SetTheory::evaluate(Init *Expr, RecSet &Elts) { + // A def in a list can be a just an element, or it may expand. + if (DefInit *Def = dynamic_cast(Expr)) { + if (const RecVec *Result = expand(Def->getDef())) + return Elts.insert(Result->begin(), Result->end()); + Elts.insert(Def->getDef()); + return; + } + + // Lists simply expand. + if (ListInit *LI = dynamic_cast(Expr)) + return evaluate(LI->begin(), LI->end(), Elts); + + // Anything else must be a DAG. + DagInit *DagExpr = dynamic_cast(Expr); + if (!DagExpr) + throw "Invalid set element: " + Expr->getAsString(); + DefInit *OpInit = dynamic_cast(DagExpr->getOperator()); + if (!OpInit) + throw "Bad set expression: " + Expr->getAsString(); + Operator *Op = Operators.lookup(OpInit->getDef()->getName()); + if (!Op) + throw "Unknown set operator: " + Expr->getAsString(); + Op->apply(*this, DagExpr, Elts); +} + +const RecVec *SetTheory::expand(Record *Set) { + // Check existing entries for Set and return early. + ExpandMap::iterator I = Expansions.find(Set); + if (I != Expansions.end()) + return &I->second; + + // This is the first time we see Set. Find a suitable expander. + try { + const std::vector &SC = Set->getSuperClasses(); + for (unsigned i = 0, e = SC.size(); i != e; ++i) + if (Expander *Exp = Expanders.lookup(SC[i]->getName())) { + // This breaks recursive definitions. + RecVec &EltVec = Expansions[Set]; + RecSet Elts; + Exp->expand(*this, Set, Elts); + EltVec.assign(Elts.begin(), Elts.end()); + return &EltVec; + } + } catch (const std::string &Error) { + throw TGError(Set->getLoc(), Error); + } + + // Set is not expandable. + return 0; +} + diff --git a/utils/TableGen/SetTheory.h b/utils/TableGen/SetTheory.h new file mode 100644 index 00000000000..c648d46e60d --- /dev/null +++ b/utils/TableGen/SetTheory.h @@ -0,0 +1,133 @@ +//===- SetTheory.h - Generate ordered sets from DAG expressions -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the SetTheory class that computes ordered sets of +// Records from DAG expressions. Operators for standard set operations are +// predefined, and it is possible to add special purpose set operators as well. +// +// The user may define named sets as Records of predefined classes. Set +// expanders can be added to a SetTheory instance to teach it how to find the +// elements of such a named set. +// +// These are the predefined operators. The argument lists can be individual +// elements (defs), other sets (defs of expandable classes), lists, or DAG +// expressions that are evaluated recursively. +// +// - (add S1, S2 ...) Union sets. This is also how sets are created from element +// lists. +// +// - (sub S1, S2, ...) Set difference. Every element in S1 except for the +// elements in S2, ... +// +// - (and S1, S2) Set intersection. Every element in S1 that is also in S2. +// +// - (shl S, N) Shift left. Remove the first N elements from S. +// +// - (trunc S, N) Truncate. The first N elements of S. +// +// - (rotl S, N) Rotate left. Same as (add (shl S, N), (trunc S, N)). +// +// - (rotr S, N) Rotate right. +// +// - (decimate S, N) Decimate S by picking every N'th element, starting with +// the first one. For instance, (decimate S, 2) returns the even elements of +// S. +// +// - (sequence "Format", From, To) Generate a sequence of defs with printf. +// For instance, (sequence "R%u", 0, 3) -> [ R0, R1, R2, R3 ] +// +//===----------------------------------------------------------------------===// + +#ifndef SETTHEORY_H +#define SETTHEORY_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/SetVector.h" +#include +#include + +namespace llvm { + +class DagInit; +struct Init; +class Record; +class RecordKeeper; + +class SetTheory { +public: + typedef std::vector RecVec; + typedef SmallSetVector RecSet; + + /// Operator - A callback representing a DAG operator. + struct Operator { + /// apply - Apply this operator to Expr's arguments and insert the result + /// in Elts. + virtual void apply(SetTheory&, DagInit *Expr, RecSet &Elts) =0; + }; + + /// Expander - A callback function that can transform a Record representing a + /// set into a fully expanded list of elements. Expanders provide a way for + /// users to define named sets that can be used in DAG expressions. + struct Expander { + virtual void expand(SetTheory&, Record*, RecSet &Elts) =0; + }; + +private: + // Map set defs to their fully expanded contents. This serves as a memoization + // cache and it makes it possible to return const references on queries. + typedef std::map ExpandMap; + ExpandMap Expansions; + + // Known DAG operators by name. + StringMap Operators; + + // Typed expanders by class name. + StringMap Expanders; + +public: + /// Create a SetTheory instance with only the standard operators. + /// A 'sequence' operator will only be added if a RecordKeeper is given. + SetTheory(RecordKeeper *Records = 0); + + /// addExpander - Add an expander for Records with the named super class. + void addExpander(StringRef ClassName, Expander*); + + /// addFieldExpander - Add an expander for ClassName that simply evaluates + /// FieldName in the Record to get the set elements. That is all that is + /// needed for a class like: + /// + /// class Set { + /// dag Elts = d; + /// } + /// + void addFieldExpander(StringRef ClassName, StringRef FieldName); + + /// addOperator - Add a DAG operator. + void addOperator(StringRef Name, Operator*); + + /// evaluate - Evaluate Expr and append the resulting set to Elts. + void evaluate(Init *Expr, RecSet &Elts); + + /// evaluate - Evaluate a sequence of Inits and append to Elts. + template + void evaluate(Iter begin, Iter end, RecSet &Elts) { + while (begin != end) + evaluate(*begin++, Elts); + } + + /// expand - Expand a record into a set of elements if possible. Return a + /// pointer to the expanded elements, or NULL if Set cannot be expanded + /// further. + const RecVec *expand(Record *Set); +}; + +} // end namespace llvm + +#endif + diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index fb941c44dcf..925b134ca4f 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -37,6 +37,7 @@ #include "RegisterInfoEmitter.h" #include "ARMDecoderEmitter.h" #include "SubtargetEmitter.h" +#include "SetTheory.h" #include "TGParser.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/Support/CommandLine.h" @@ -80,7 +81,8 @@ enum ActionType { GenArmNeon, GenArmNeonSema, GenArmNeonTest, - PrintEnums + PrintEnums, + PrintSets }; namespace { @@ -162,6 +164,8 @@ namespace { "Generate ARM NEON tests for clang"), clEnumValN(PrintEnums, "print-enums", "Print enum values for a class"), + clEnumValN(PrintSets, "print-sets", + "Print expanded sets for testing DAG exprs"), clEnumValEnd)); cl::opt @@ -374,6 +378,21 @@ int main(int argc, char **argv) { Out.os() << "\n"; break; } + case PrintSets: + { + SetTheory Sets(&Records); + Sets.addFieldExpander("Set", "Elements"); + std::vector Recs = Records.getAllDerivedDefinitions("Set"); + for (unsigned i = 0, e = Recs.size(); i != e; ++i) { + Out.os() << Recs[i]->getName() << " = ["; + const std::vector *Elts = Sets.expand(Recs[i]); + assert(Elts && "Couldn't expand Set instance"); + for (unsigned ei = 0, ee = Elts->size(); ei != ee; ++ei) + Out.os() << ' ' << (*Elts)[ei]->getName(); + Out.os() << " ]\n"; + } + break; + } default: assert(1 && "Invalid Action"); return 1;