//===- ParseTestSpecification.h - Parsing for test instructions -*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines test::Argument. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_PARSETESTSPECIFICATION #define SWIFT_SIL_PARSETESTSPECIFICATION #include "swift/Basic/TaggedUnion.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILValue.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FormattedStream.h" namespace llvm { template class SmallVectorImpl; } using llvm::StringRef; namespace swift { class SILFunction; class SILArgument; namespace test { struct Argument { enum class Kind { String, Bool, UInt, Value, Operand, Instruction, BlockArgument, Block, Function, }; using Union = TaggedUnion; private: Kind kind; protected: Argument(Union storage, Kind kind) : kind(kind), storage(storage) {} Union storage; public: Kind getKind() const { return kind; } void print(llvm::raw_ostream &os); #ifndef NDEBUG void dump() { print(llvm::errs()); } #endif }; template struct ConcreteArgument : Argument { using Stored = _Stored; using Super = ConcreteArgument; ConcreteArgument(Stored stored) : Argument(Union{stored}, TheKind) {} Stored getValue() { return storage.get(); } static bool classof(const Argument *argument) { return argument->getKind() == TheKind; } }; struct ValueArgument : ConcreteArgument { ValueArgument(SILValue stored) : Super(stored) {} }; struct OperandArgument : ConcreteArgument { OperandArgument(Operand *stored) : Super(stored) {} }; struct InstructionArgument : ConcreteArgument { InstructionArgument(SILInstruction *stored) : Super(stored) {} }; struct BlockArgumentArgument : ConcreteArgument { BlockArgumentArgument(SILArgument *stored) : Super(stored) {} }; struct BlockArgument : ConcreteArgument { BlockArgument(SILBasicBlock *stored) : Super(stored) {} }; struct FunctionArgument : ConcreteArgument { FunctionArgument(SILFunction *stored) : Super(stored) {} }; struct BoolArgument : ConcreteArgument { BoolArgument(bool stored) : Super(stored) {} }; struct UIntArgument : ConcreteArgument { UIntArgument(unsigned long long stored) : Super(stored) {} }; struct StringArgument : ConcreteArgument { StringArgument(StringRef stored) : Super(stored) {} }; struct Arguments { llvm::SmallVector storage; unsigned untakenIndex = 0; void assertUsed() { assert(untakenIndex == storage.size() && "arguments remain!"); } void clear() { assertUsed(); storage.clear(); untakenIndex = 0; } bool hasUntaken() { return untakenIndex < storage.size(); } Arguments() {} Arguments(Arguments const &) = delete; Arguments &operator=(Arguments const &) = delete; ~Arguments() { assertUsed(); } Argument &takeArgument() { return storage[untakenIndex++]; } private: template typename Subtype::Stored getInstance(StringRef name, Argument &argument) { if (isa(argument)) { auto stored = cast(argument).getValue(); return stored; } llvm::errs() << "Attempting to take a " << name << " argument but have\n"; argument.print(llvm::errs()); llvm::report_fatal_error("Bad unit test"); } template typename Subtype::Stored takeInstance(StringRef name) { auto argument = takeArgument(); return getInstance(name, argument); } public: StringRef takeString() { return takeInstance("string"); } bool takeBool() { return takeInstance("bool"); } unsigned long long takeUInt() { return takeInstance("uint"); } SILValue takeValue() { auto argument = takeArgument(); if (isa(argument)) { auto *instruction = cast(argument).getValue(); auto *svi = cast(instruction); return svi; } else if (isa(argument)) { auto *arg = cast(argument).getValue(); return arg; } return getInstance("value", argument); } Operand *takeOperand() { return takeInstance("operand"); } SILInstruction *takeInstruction() { auto argument = takeArgument(); if (isa(argument)) { auto value = cast(argument).getValue(); auto *definingInstruction = value.getDefiningInstruction(); assert(definingInstruction && "selected instruction via argument value!?"); return definingInstruction; } return getInstance("instruction", argument); } SILArgument *takeBlockArgument() { return takeInstance("block argument"); } SILBasicBlock *takeBlock() { return takeInstance("block"); } SILFunction *takeFunction() { return takeInstance("block"); } }; /// The specification for a test which has not yet been parsed. struct UnparsedSpecification { /// The string which specifies the test. /// /// Not a StringRef because the SpecifyTestInst whose payload is of /// interest gets deleted. std::string string; /// The next non-debug instruction. /// /// Provides an "anchor" for the specification. Contextual arguments /// (@{instruction|block|function}) can be parsed in terms of this /// anchor. SILInstruction *context; /// Map from names used in the specification to the corresponding SILValues. llvm::StringMap values; }; /// Populates the array \p components with the elements of \p /// specificationString. void getTestSpecificationComponents(StringRef specificationString, SmallVectorImpl &components); /// Finds and deletes each specify_test instruction in \p function and /// appends its string payload to the provided vector. void getTestSpecifications( SILFunction *function, SmallVectorImpl &specifications); /// Given the string \p specification operand of a specify_test /// instruction from \p function, parse the arguments which it refers to into /// \p arguments and the component strings into \p argumentStrings. void parseTestArgumentsFromSpecification( SILFunction *function, UnparsedSpecification const &specification, Arguments &arguments, SmallVectorImpl &argumentStrings); } // namespace test } // namespace swift #endif