blob: 3b0bc24cbbf8044a153b1fe0024f8e3c5a1d33a4 [file] [log] [blame]
//===--- 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