| //===--- Explosion.h - Exploded R-Value Representation ----------*- C++ -*-===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // A storage structure for holding an exploded r-value. An exploded |
| // r-value has been separated into individual components. Only types |
| // with non-resilient structure may be exploded. |
| // |
| // The standard use for explosions is for argument-passing. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_IRGEN_EXPLOSION_H |
| #define SWIFT_IRGEN_EXPLOSION_H |
| |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "IRGen.h" |
| #include "IRGenFunction.h" |
| |
| namespace swift { |
| namespace irgen { |
| |
| /// The representation for an explosion is just a list of raw LLVM |
| /// values. The meaning of these values is imposed externally by the |
| /// type infos, except that it is expected that they will be passed |
| /// as arguments in exactly this way. |
| class Explosion { |
| unsigned NextValue; |
| SmallVector<llvm::Value*, 8> Values; |
| |
| public: |
| Explosion() : NextValue(0) {} |
| |
| // We want to be a move-only type. |
| Explosion(const Explosion &) = delete; |
| Explosion &operator=(const Explosion &) = delete; |
| Explosion(Explosion &&other) : NextValue(0) { |
| // Do an uninitialized copy of the non-consumed elements. |
| Values.reserve(other.size()); |
| Values.set_size(other.size()); |
| std::uninitialized_copy(other.begin(), other.end(), Values.begin()); |
| |
| // Remove everything from the other explosion. |
| other.reset(); |
| } |
| |
| Explosion &operator=(Explosion &&o) { |
| assert(empty() && "explosion had values remaining when reassigned!"); |
| NextValue = o.NextValue; |
| Values.swap(o.Values); |
| o.reset(); |
| return *this; |
| } |
| |
| ~Explosion() { |
| assert(empty() && "explosion had values remaining when destroyed!"); |
| } |
| |
| bool empty() const { |
| return NextValue == Values.size(); |
| } |
| |
| size_t size() const { |
| return Values.size() - NextValue; |
| } |
| |
| typedef SmallVector<llvm::Value*, 8>::iterator iterator; |
| iterator begin() { return Values.begin() + NextValue; } |
| iterator end() { return Values.end(); } |
| |
| typedef SmallVector<llvm::Value*, 8>::const_iterator const_iterator; |
| const_iterator begin() const { return Values.begin() + NextValue; } |
| const_iterator end() const { return Values.end(); } |
| |
| /// Add a value to the end of this exploded r-value. |
| void add(llvm::Value *value) { |
| assert(value && "adding null value to explosion"); |
| assert(NextValue == 0 && "adding to partially-claimed explosion?"); |
| Values.push_back(value); |
| } |
| |
| void add(ArrayRef<llvm::Value*> values) { |
| #ifndef NDEBUG |
| for (auto value : values) |
| assert(value && "adding null value to explosion"); |
| #endif |
| assert(NextValue == 0 && "adding to partially-claimed explosion?"); |
| Values.append(values.begin(), values.end()); |
| } |
| |
| /// Return an array containing the given range of values. The values |
| /// are not claimed. |
| ArrayRef<llvm::Value*> getRange(unsigned from, unsigned to) const { |
| assert(from <= to); |
| assert(to <= Values.size()); |
| return llvm::makeArrayRef(begin() + from, to - from); |
| } |
| |
| /// Return an array containing all of the remaining values. The values |
| /// are not claimed. |
| ArrayRef<llvm::Value *> getAll() { |
| return llvm::makeArrayRef(begin(), Values.size() - NextValue); |
| } |
| |
| /// Transfer ownership of the next N values to the given explosion. |
| void transferInto(Explosion &other, unsigned n) { |
| other.add(claim(n)); |
| } |
| |
| |
| /// The next N values have been claimed in some indirect way (e.g. |
| /// using getRange() and the like); just give up on them. |
| void markClaimed(unsigned n) { |
| assert(NextValue + n <= Values.size()); |
| NextValue += n; |
| } |
| |
| /// Claim and return the next value in this explosion. |
| llvm::Value *claimNext() { |
| assert(NextValue < Values.size()); |
| return Values[NextValue++]; |
| } |
| |
| /// Claim and return the next N values in this explosion. |
| ArrayRef<llvm::Value*> claim(unsigned n) { |
| assert(NextValue + n <= Values.size()); |
| auto array = llvm::makeArrayRef(begin(), n); |
| NextValue += n; |
| return array; |
| } |
| |
| /// Claim and return all the values in this explosion. |
| ArrayRef<llvm::Value*> claimAll() { |
| return claim(size()); |
| } |
| |
| // These are all kindof questionable. |
| |
| /// Without changing any state, take the last claimed value, |
| /// if there is one. |
| llvm::Value *getLastClaimed() { |
| assert(NextValue > 0); |
| return Values[NextValue-1]; |
| } |
| |
| /// Claim and remove the last item in the array. |
| /// Unlike the normal 'claim' methods, the item is gone forever. |
| llvm::Value *takeLast() { |
| assert(!empty()); |
| auto result = Values.back(); |
| Values.pop_back(); |
| return result; |
| } |
| |
| /// Reset this explosion. |
| void reset() { |
| NextValue = 0; |
| Values.clear(); |
| } |
| |
| void print(llvm::raw_ostream &OS); |
| void dump(); |
| }; |
| |
| /// An explosion schema is essentially the type of an Explosion. |
| class ExplosionSchema { |
| public: |
| |
| /// The schema for one atom of the explosion. |
| class Element { |
| llvm::Type *Type; |
| Alignment::int_type Align; |
| Element() = default; |
| public: |
| static Element forScalar(llvm::Type *type) { |
| Element e; |
| e.Type = type; |
| e.Align = 0; |
| return e; |
| } |
| |
| static Element forAggregate(llvm::Type *type, Alignment align) { |
| assert(align.getValue() != 0 && "alignment with zero value!"); |
| Element e; |
| e.Type = type; |
| e.Align = align.getValue(); |
| return e; |
| } |
| |
| bool isScalar() const { return Align == 0; } |
| llvm::Type *getScalarType() const { assert(isScalar()); return Type; } |
| |
| bool isAggregate() const { return !isScalar(); } |
| llvm::Type *getAggregateType() const { |
| assert(isAggregate()); |
| return Type; |
| } |
| Alignment getAggregateAlignment() const { |
| assert(isAggregate()); |
| return Alignment(Align); |
| } |
| }; |
| |
| private: |
| SmallVector<Element, 8> Elements; |
| bool ContainsAggregate; |
| |
| public: |
| ExplosionSchema() : ContainsAggregate(false) {} |
| |
| /// Return the number of elements in this schema. |
| unsigned size() const { return Elements.size(); } |
| bool empty() const { return Elements.empty(); } |
| |
| /// Does this schema contain an aggregate element? |
| bool containsAggregate() const { return ContainsAggregate; } |
| |
| /// Does this schema consist solely of one aggregate element? |
| bool isSingleAggregate() const { |
| return size() == 1 && containsAggregate(); |
| } |
| |
| const Element &operator[](size_t index) const { |
| return Elements[index]; |
| } |
| |
| typedef SmallVectorImpl<Element>::iterator iterator; |
| typedef SmallVectorImpl<Element>::const_iterator const_iterator; |
| |
| iterator begin() { return Elements.begin(); } |
| iterator end() { return Elements.end(); } |
| const_iterator begin() const { return Elements.begin(); } |
| const_iterator end() const { return Elements.end(); } |
| |
| void add(Element e) { |
| Elements.push_back(e); |
| ContainsAggregate |= e.isAggregate(); |
| } |
| |
| /// Produce the correct type for a direct return of this schema, |
| /// which is assumed to contain only scalars. This is defined as: |
| /// - void, if the schema is empty; |
| /// - the element type, if the schema contains exactly one element; |
| /// - an anonymous struct type concatenating those types, otherwise. |
| llvm::Type *getScalarResultType(IRGenModule &IGM) const; |
| }; |
| |
| } // end namespace irgen |
| } // end namespace swift |
| |
| #endif |