mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:10:14 +01:00
Replace FoldingSet with DenseMap for instruction canonicalization. (#3979)
Switch from recursing into non-canonical instruction fields to separately canonicalizing those fields. This means we now form canonical `InstBlockId`s, `TypeBlockId`s, `IntId`s, `FloatId`s, and `BindNameId`s at least in the cases when they're referenced by a constant instruction. This reduces the overall runtime for @chandlerc's 10MLoC example by 27.5% on my machine.
This commit is contained in:
@@ -279,6 +279,16 @@ class Hasher {
|
||||
// state along with the contents.
|
||||
auto HashSizedBytes(llvm::ArrayRef<std::byte> bytes) -> void;
|
||||
|
||||
// Incorporate a dynamically sized sequence of bytes represented as an array
|
||||
// of objects into the hasher's state.
|
||||
template <typename T>
|
||||
requires std::has_unique_object_representations_v<T>
|
||||
auto HashSizedBytes(llvm::ArrayRef<T> data) -> void {
|
||||
HashSizedBytes(llvm::ArrayRef<std::byte>(
|
||||
reinterpret_cast<const std::byte*>(data.data()),
|
||||
data.size() * sizeof(T)));
|
||||
}
|
||||
|
||||
// An out-of-line, throughput-optimized routine for incorporating a
|
||||
// dynamically sized sequence when the sequence size is guaranteed to be >32.
|
||||
// The size is always incorporated into the state.
|
||||
|
||||
@@ -283,6 +283,7 @@ struct HashableType {
|
||||
|
||||
int ignored = 0;
|
||||
|
||||
// See common/hashing.h.
|
||||
friend auto CarbonHashValue(const HashableType& value, uint64_t seed)
|
||||
-> HashCode {
|
||||
Hasher hasher(seed);
|
||||
|
||||
@@ -38,6 +38,7 @@ cc_library(
|
||||
":index_base",
|
||||
":yaml",
|
||||
"//common:check",
|
||||
"//common:hashing",
|
||||
"//common:ostream",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/hashing.h"
|
||||
#include "common/ostream.h"
|
||||
#include "llvm/ADT/APFloat.h"
|
||||
#include "llvm/ADT/APInt.h"
|
||||
@@ -21,13 +22,13 @@
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// The value of a real literal.
|
||||
// The value of a real literal token.
|
||||
//
|
||||
// This is either a dyadic fraction (mantissa * 2^exponent) or a decadic
|
||||
// fraction (mantissa * 10^exponent).
|
||||
//
|
||||
// TODO: For SemIR, replace this with a Rational type, per the design:
|
||||
// docs/design/expressions/literals.md
|
||||
// These values are not canonicalized, because we don't expect them to repeat
|
||||
// and don't use them in SemIR values.
|
||||
class Real : public Printable<Real> {
|
||||
public:
|
||||
auto Print(llvm::raw_ostream& output_stream) const -> void {
|
||||
@@ -48,9 +49,12 @@ class Real : public Printable<Real> {
|
||||
bool is_decimal;
|
||||
};
|
||||
|
||||
// Corresponds to an integer value represented by an APInt.
|
||||
// Corresponds to an integer value represented by an APInt. This is used both
|
||||
// for integer literal tokens, which are unsigned and have an unspecified
|
||||
// bit-width, and integer values in SemIR, which have a signedness and bit-width
|
||||
// matching their type.
|
||||
struct IntId : public IdBase, public Printable<IntId> {
|
||||
using ValueType = const llvm::APInt;
|
||||
using ValueType = llvm::APInt;
|
||||
static const IntId Invalid;
|
||||
using IdBase::IdBase;
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
@@ -60,9 +64,10 @@ struct IntId : public IdBase, public Printable<IntId> {
|
||||
};
|
||||
constexpr IntId IntId::Invalid(IntId::InvalidIndex);
|
||||
|
||||
// Corresponds to a float value represented by an APFloat.
|
||||
// Corresponds to a float value represented by an APFloat. This is used for
|
||||
// floating-point values in SemIR.
|
||||
struct FloatId : public IdBase, public Printable<FloatId> {
|
||||
using ValueType = const llvm::APFloat;
|
||||
using ValueType = llvm::APFloat;
|
||||
static const FloatId Invalid;
|
||||
using IdBase::IdBase;
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
@@ -72,9 +77,29 @@ struct FloatId : public IdBase, public Printable<FloatId> {
|
||||
};
|
||||
constexpr FloatId FloatId::Invalid(FloatId::InvalidIndex);
|
||||
|
||||
// DenseMapInfo for llvm::APFloat, for use in the canonical float value store.
|
||||
// LLVM has an implementation of this but it strangely lives in the non-public
|
||||
// header lib/IR/LLVMContextImpl.h, so we use our own.
|
||||
// TODO: Remove this once our new hash table is available.
|
||||
struct APFloatDenseMapInfo {
|
||||
static auto getEmptyKey() -> llvm::APFloat {
|
||||
return llvm::APFloat(llvm::APFloat::Bogus(), 1);
|
||||
}
|
||||
static auto getTombstoneKey() -> llvm::APFloat {
|
||||
return llvm::APFloat(llvm::APFloat::Bogus(), 2);
|
||||
}
|
||||
static auto getHashValue(const llvm::APFloat& val) -> unsigned {
|
||||
return hash_value(val);
|
||||
}
|
||||
static auto isEqual(const llvm::APFloat& lhs, const llvm::APFloat& rhs)
|
||||
-> bool {
|
||||
return lhs.bitwiseIsEqual(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
// Corresponds to a Real value.
|
||||
struct RealId : public IdBase, public Printable<RealId> {
|
||||
using ValueType = const Real;
|
||||
using ValueType = Real;
|
||||
static const RealId Invalid;
|
||||
using IdBase::IdBase;
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
@@ -86,7 +111,7 @@ constexpr RealId RealId::Invalid(RealId::InvalidIndex);
|
||||
|
||||
// Corresponds to a StringRef.
|
||||
struct StringId : public IdBase, public Printable<StringId> {
|
||||
using ValueType = const std::string;
|
||||
using ValueType = std::string;
|
||||
static const StringId Invalid;
|
||||
using IdBase::IdBase;
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
@@ -191,10 +216,62 @@ class ValueStore
|
||||
llvm::SmallVector<std::decay_t<ValueType>, 0> values_;
|
||||
};
|
||||
|
||||
// A wrapper for accumulating immutable values with deduplication, providing IDs
|
||||
// to later retrieve the value.
|
||||
//
|
||||
// IdT::ValueType must represent the type being indexed.
|
||||
template <typename IdT,
|
||||
typename DenseMapInfoT = llvm::DenseMapInfo<typename IdT::ValueType>>
|
||||
class CanonicalValueStore {
|
||||
public:
|
||||
using ValueType = typename IdT::ValueType;
|
||||
|
||||
// Stores a canonical copy of the value and returns an ID to reference it.
|
||||
auto Add(ValueType value) -> IdT {
|
||||
auto [it, added] = map_.insert({value, IdT::Invalid});
|
||||
if (added) {
|
||||
it->second = values_.Add(value);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Returns the value for an ID.
|
||||
auto Get(IdT id) const -> const ValueType& { return values_.Get(id); }
|
||||
|
||||
// Reserves space.
|
||||
auto Reserve(size_t size) -> void {
|
||||
values_.Reserve(size);
|
||||
map_.reserve(size);
|
||||
}
|
||||
|
||||
// These are to support printable structures, and are not guaranteed.
|
||||
auto OutputYaml() const -> Yaml::OutputMapping {
|
||||
return values_.OutputYaml();
|
||||
}
|
||||
|
||||
auto array_ref() const -> llvm::ArrayRef<ValueType> {
|
||||
return values_.array_ref();
|
||||
}
|
||||
auto size() const -> size_t { return values_.size(); }
|
||||
|
||||
private:
|
||||
// We store two copies of each value: one in the ValueStore for fast mapping
|
||||
// from IdT to value, and another in the DenseMap for the reverse mapping.
|
||||
//
|
||||
// We could avoid this by storing the map instead as a DenseSet<IdT>, but
|
||||
// there's no way to provide a DenseMapInfo that looks the IDs up in the
|
||||
// vector.
|
||||
//
|
||||
// TODO: Switch to a better representation that avoids the duplication.
|
||||
ValueStore<IdT> values_;
|
||||
llvm::DenseMap<ValueType, IdT, DenseMapInfoT> map_;
|
||||
};
|
||||
|
||||
// Storage for StringRefs. The caller is responsible for ensuring storage is
|
||||
// allocated.
|
||||
template <>
|
||||
class ValueStore<StringId> : public Yaml::Printable<ValueStore<StringId>> {
|
||||
class CanonicalValueStore<StringId>
|
||||
: public Yaml::Printable<CanonicalValueStore<StringId>> {
|
||||
public:
|
||||
// Returns an ID to reference the value. May return an existing ID if the
|
||||
// string was previously added.
|
||||
@@ -244,7 +321,8 @@ class ValueStore<StringId> : public Yaml::Printable<ValueStore<StringId>> {
|
||||
template <typename IdT>
|
||||
class StringStoreWrapper : public Printable<StringStoreWrapper<IdT>> {
|
||||
public:
|
||||
explicit StringStoreWrapper(ValueStore<StringId>* values) : values_(values) {}
|
||||
explicit StringStoreWrapper(CanonicalValueStore<StringId>* values)
|
||||
: values_(values) {}
|
||||
|
||||
auto Add(llvm::StringRef value) -> IdT {
|
||||
return IdT(values_->Add(value).index);
|
||||
@@ -263,9 +341,11 @@ class StringStoreWrapper : public Printable<StringStoreWrapper<IdT>> {
|
||||
auto size() const -> size_t { return values_->size(); }
|
||||
|
||||
private:
|
||||
ValueStore<StringId>* values_;
|
||||
CanonicalValueStore<StringId>* values_;
|
||||
};
|
||||
|
||||
using FloatValueStore = CanonicalValueStore<FloatId, APFloatDenseMapInfo>;
|
||||
|
||||
// Stores that will be used across compiler phases for a given compilation unit.
|
||||
// This is provided mainly so that they don't need to be passed separately.
|
||||
class SharedValueStores : public Yaml::Printable<SharedValueStores> {
|
||||
@@ -283,12 +363,12 @@ class SharedValueStores : public Yaml::Printable<SharedValueStores> {
|
||||
auto identifiers() const -> const StringStoreWrapper<IdentifierId>& {
|
||||
return identifiers_;
|
||||
}
|
||||
auto ints() -> ValueStore<IntId>& { return ints_; }
|
||||
auto ints() const -> const ValueStore<IntId>& { return ints_; }
|
||||
auto ints() -> CanonicalValueStore<IntId>& { return ints_; }
|
||||
auto ints() const -> const CanonicalValueStore<IntId>& { return ints_; }
|
||||
auto reals() -> ValueStore<RealId>& { return reals_; }
|
||||
auto reals() const -> const ValueStore<RealId>& { return reals_; }
|
||||
auto floats() -> ValueStore<FloatId>& { return floats_; }
|
||||
auto floats() const -> const ValueStore<FloatId>& { return floats_; }
|
||||
auto floats() -> FloatValueStore& { return floats_; }
|
||||
auto floats() const -> const FloatValueStore& { return floats_; }
|
||||
auto string_literal_values() -> StringStoreWrapper<StringLiteralValueId>& {
|
||||
return string_literal_values_;
|
||||
}
|
||||
@@ -313,11 +393,11 @@ class SharedValueStores : public Yaml::Printable<SharedValueStores> {
|
||||
}
|
||||
|
||||
private:
|
||||
ValueStore<IntId> ints_;
|
||||
CanonicalValueStore<IntId> ints_;
|
||||
ValueStore<RealId> reals_;
|
||||
ValueStore<FloatId> floats_;
|
||||
FloatValueStore floats_;
|
||||
|
||||
ValueStore<StringId> strings_;
|
||||
CanonicalValueStore<StringId> strings_;
|
||||
StringStoreWrapper<IdentifierId> identifiers_;
|
||||
StringStoreWrapper<StringLiteralValueId> string_literal_values_;
|
||||
};
|
||||
|
||||
@@ -1076,10 +1076,8 @@ auto Context::GetStructType(SemIR::InstBlockId refs_id) -> SemIR::TypeId {
|
||||
|
||||
auto Context::GetTupleType(llvm::ArrayRef<SemIR::TypeId> type_ids)
|
||||
-> SemIR::TypeId {
|
||||
// TODO: Deduplicate the type block here. Currently requesting the same tuple
|
||||
// type more than once will create multiple type blocks, all but one of which
|
||||
// is unused.
|
||||
return GetTypeImpl<SemIR::TupleType>(*this, type_blocks().Add(type_ids));
|
||||
return GetTypeImpl<SemIR::TupleType>(*this,
|
||||
type_blocks().AddCanonical(type_ids));
|
||||
}
|
||||
|
||||
auto Context::GetAssociatedEntityType(SemIR::InterfaceId interface_id,
|
||||
|
||||
@@ -341,15 +341,13 @@ class Context {
|
||||
auto identifiers() -> StringStoreWrapper<IdentifierId>& {
|
||||
return sem_ir().identifiers();
|
||||
}
|
||||
auto ints() -> ValueStore<IntId>& { return sem_ir().ints(); }
|
||||
auto ints() -> CanonicalValueStore<IntId>& { return sem_ir().ints(); }
|
||||
auto reals() -> ValueStore<RealId>& { return sem_ir().reals(); }
|
||||
auto floats() -> ValueStore<FloatId>& { return sem_ir().floats(); }
|
||||
auto floats() -> FloatValueStore& { return sem_ir().floats(); }
|
||||
auto string_literal_values() -> StringStoreWrapper<StringLiteralValueId>& {
|
||||
return sem_ir().string_literal_values();
|
||||
}
|
||||
auto bind_names() -> ValueStore<SemIR::BindNameId>& {
|
||||
return sem_ir().bind_names();
|
||||
}
|
||||
auto bind_names() -> SemIR::BindNameStore& { return sem_ir().bind_names(); }
|
||||
auto functions() -> ValueStore<SemIR::FunctionId>& {
|
||||
return sem_ir().functions();
|
||||
}
|
||||
|
||||
@@ -141,9 +141,9 @@ static auto GetConstantValue(Context& context, SemIR::InstBlockId inst_block_id,
|
||||
|
||||
const_insts.push_back(const_inst_id);
|
||||
}
|
||||
// TODO: If the new block is identical to the original block, return the
|
||||
// original ID.
|
||||
return context.inst_blocks().Add(const_insts);
|
||||
// TODO: If the new block is identical to the original block, and we know the
|
||||
// old ID was canonical, return the original ID.
|
||||
return context.inst_blocks().AddCanonical(const_insts);
|
||||
}
|
||||
|
||||
// The constant value of a type block is that type block, but we still need to
|
||||
@@ -1054,6 +1054,7 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst)
|
||||
case CARBON_KIND(SemIR::BindSymbolicName bind): {
|
||||
// The constant form of a symbolic binding is an idealized form of the
|
||||
// original, with no equivalent value.
|
||||
bind.bind_name_id = context.bind_names().MakeCanonical(bind.bind_name_id);
|
||||
bind.value_id = SemIR::InstId::Invalid;
|
||||
return MakeConstantResult(context, bind, Phase::Symbolic);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ static auto PopOperand(Context& context, Worklist& worklist, SemIR::IdKind kind,
|
||||
for (auto i : llvm::reverse(llvm::seq(size))) {
|
||||
new_inst_block.Set(i, worklist.Pop());
|
||||
}
|
||||
return new_inst_block.id().index;
|
||||
return new_inst_block.GetCanonical().index;
|
||||
}
|
||||
case SemIR::IdKind::For<SemIR::TypeBlockId>: {
|
||||
auto old_type_block_id = static_cast<SemIR::TypeBlockId>(arg);
|
||||
@@ -119,7 +119,7 @@ static auto PopOperand(Context& context, Worklist& worklist, SemIR::IdKind kind,
|
||||
new_type_block.Set(size - i - 1,
|
||||
context.GetTypeIdForTypeInst(worklist.Pop()));
|
||||
}
|
||||
return new_type_block.id().index;
|
||||
return new_type_block.GetCanonical().index;
|
||||
}
|
||||
default:
|
||||
return arg;
|
||||
|
||||
-2
@@ -34,7 +34,6 @@ fn B() {}
|
||||
// CHECK:STDOUT: type2: {constant: template inst+3, value_rep: {kind: none, type: type2}}
|
||||
// CHECK:STDOUT: type_blocks:
|
||||
// CHECK:STDOUT: typeBlock0: {}
|
||||
// CHECK:STDOUT: typeBlock1: {}
|
||||
// CHECK:STDOUT: insts:
|
||||
// CHECK:STDOUT: 'inst+0': {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
|
||||
// CHECK:STDOUT: 'inst+1': {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1}
|
||||
@@ -96,7 +95,6 @@ fn B() {}
|
||||
// CHECK:STDOUT: type2: {constant: template inst+3, value_rep: {kind: none, type: type2}}
|
||||
// CHECK:STDOUT: type_blocks:
|
||||
// CHECK:STDOUT: typeBlock0: {}
|
||||
// CHECK:STDOUT: typeBlock1: {}
|
||||
// CHECK:STDOUT: insts:
|
||||
// CHECK:STDOUT: 'inst+0': {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
|
||||
// CHECK:STDOUT: 'inst+1': {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1}
|
||||
|
||||
@@ -34,7 +34,6 @@ fn B() {}
|
||||
// CHECK:STDOUT: type2: {constant: template inst+3, value_rep: {kind: none, type: type2}}
|
||||
// CHECK:STDOUT: type_blocks:
|
||||
// CHECK:STDOUT: typeBlock0: {}
|
||||
// CHECK:STDOUT: typeBlock1: {}
|
||||
// CHECK:STDOUT: insts:
|
||||
// CHECK:STDOUT: 'inst+0': {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
|
||||
// CHECK:STDOUT: 'inst+1': {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1}
|
||||
@@ -75,7 +74,6 @@ fn B() {}
|
||||
// CHECK:STDOUT: type2: {constant: template inst+3, value_rep: {kind: none, type: type2}}
|
||||
// CHECK:STDOUT: type_blocks:
|
||||
// CHECK:STDOUT: typeBlock0: {}
|
||||
// CHECK:STDOUT: typeBlock1: {}
|
||||
// CHECK:STDOUT: insts:
|
||||
// CHECK:STDOUT: 'inst+0': {kind: Namespace, arg0: name_scope0, arg1: inst<invalid>, type: type0}
|
||||
// CHECK:STDOUT: 'inst+1': {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1}
|
||||
|
||||
@@ -31,21 +31,7 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: type4: {constant: template inst+17, value_rep: {kind: copy, type: type4}}
|
||||
// CHECK:STDOUT: type_blocks:
|
||||
// CHECK:STDOUT: typeBlock0: {}
|
||||
// CHECK:STDOUT: typeBlock1: {}
|
||||
// CHECK:STDOUT: typeBlock2: {}
|
||||
// CHECK:STDOUT: typeBlock3: {}
|
||||
// CHECK:STDOUT: typeBlock4:
|
||||
// CHECK:STDOUT: 0: type1
|
||||
// CHECK:STDOUT: 1: type1
|
||||
// CHECK:STDOUT: typeBlock5: {}
|
||||
// CHECK:STDOUT: typeBlock6: {}
|
||||
// CHECK:STDOUT: typeBlock7:
|
||||
// CHECK:STDOUT: 0: type1
|
||||
// CHECK:STDOUT: 1: type1
|
||||
// CHECK:STDOUT: typeBlock8: {}
|
||||
// CHECK:STDOUT: typeBlock9: {}
|
||||
// CHECK:STDOUT: typeBlock10: {}
|
||||
// CHECK:STDOUT: typeBlock11:
|
||||
// CHECK:STDOUT: typeBlock1:
|
||||
// CHECK:STDOUT: 0: type1
|
||||
// CHECK:STDOUT: 1: type1
|
||||
// CHECK:STDOUT: insts:
|
||||
@@ -57,7 +43,7 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: 'inst+5': {kind: BindName, arg0: bindName0, arg1: inst+4, type: type1}
|
||||
// CHECK:STDOUT: 'inst+6': {kind: TupleLiteral, arg0: empty, type: type1}
|
||||
// CHECK:STDOUT: 'inst+7': {kind: TupleLiteral, arg0: empty, type: type1}
|
||||
// CHECK:STDOUT: 'inst+8': {kind: TupleType, arg0: typeBlock4, type: typeTypeType}
|
||||
// CHECK:STDOUT: 'inst+8': {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
|
||||
// CHECK:STDOUT: 'inst+9': {kind: TupleLiteral, arg0: block4, type: type2}
|
||||
// CHECK:STDOUT: 'inst+10': {kind: Converted, arg0: inst+6, arg1: inst+1, type: typeTypeType}
|
||||
// CHECK:STDOUT: 'inst+11': {kind: Converted, arg0: inst+7, arg1: inst+1, type: typeTypeType}
|
||||
@@ -78,7 +64,7 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: 'inst+26': {kind: TupleInit, arg0: empty, arg1: inst+25, type: type1}
|
||||
// CHECK:STDOUT: 'inst+27': {kind: Converted, arg0: inst+19, arg1: inst+26, type: type1}
|
||||
// CHECK:STDOUT: 'inst+28': {kind: TupleInit, arg0: block10, arg1: inst+13, type: type2}
|
||||
// CHECK:STDOUT: 'inst+29': {kind: TupleValue, arg0: block12, type: type2}
|
||||
// CHECK:STDOUT: 'inst+29': {kind: TupleValue, arg0: block11, type: type2}
|
||||
// CHECK:STDOUT: 'inst+30': {kind: Converted, arg0: inst+20, arg1: inst+28, type: type2}
|
||||
// CHECK:STDOUT: 'inst+31': {kind: ReturnExpr, arg0: inst+30, arg1: inst+13}
|
||||
// CHECK:STDOUT: constant_values:
|
||||
@@ -144,11 +130,10 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: block10:
|
||||
// CHECK:STDOUT: 0: inst+24
|
||||
// CHECK:STDOUT: 1: inst+27
|
||||
// CHECK:STDOUT: block11: {}
|
||||
// CHECK:STDOUT: block12:
|
||||
// CHECK:STDOUT: block11:
|
||||
// CHECK:STDOUT: 0: inst+23
|
||||
// CHECK:STDOUT: 1: inst+23
|
||||
// CHECK:STDOUT: block13:
|
||||
// CHECK:STDOUT: block12:
|
||||
// CHECK:STDOUT: 0: inst+0
|
||||
// CHECK:STDOUT: 1: inst+14
|
||||
// CHECK:STDOUT: ...
|
||||
|
||||
+5
-20
@@ -31,21 +31,7 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: type4: {constant: template inst+17, value_rep: {kind: copy, type: type4}}
|
||||
// CHECK:STDOUT: type_blocks:
|
||||
// CHECK:STDOUT: typeBlock0: {}
|
||||
// CHECK:STDOUT: typeBlock1: {}
|
||||
// CHECK:STDOUT: typeBlock2: {}
|
||||
// CHECK:STDOUT: typeBlock3: {}
|
||||
// CHECK:STDOUT: typeBlock4:
|
||||
// CHECK:STDOUT: 0: type1
|
||||
// CHECK:STDOUT: 1: type1
|
||||
// CHECK:STDOUT: typeBlock5: {}
|
||||
// CHECK:STDOUT: typeBlock6: {}
|
||||
// CHECK:STDOUT: typeBlock7:
|
||||
// CHECK:STDOUT: 0: type1
|
||||
// CHECK:STDOUT: 1: type1
|
||||
// CHECK:STDOUT: typeBlock8: {}
|
||||
// CHECK:STDOUT: typeBlock9: {}
|
||||
// CHECK:STDOUT: typeBlock10: {}
|
||||
// CHECK:STDOUT: typeBlock11:
|
||||
// CHECK:STDOUT: typeBlock1:
|
||||
// CHECK:STDOUT: 0: type1
|
||||
// CHECK:STDOUT: 1: type1
|
||||
// CHECK:STDOUT: insts:
|
||||
@@ -57,7 +43,7 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: 'inst+5': {kind: BindName, arg0: bindName0, arg1: inst+4, type: type1}
|
||||
// CHECK:STDOUT: 'inst+6': {kind: TupleLiteral, arg0: empty, type: type1}
|
||||
// CHECK:STDOUT: 'inst+7': {kind: TupleLiteral, arg0: empty, type: type1}
|
||||
// CHECK:STDOUT: 'inst+8': {kind: TupleType, arg0: typeBlock4, type: typeTypeType}
|
||||
// CHECK:STDOUT: 'inst+8': {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
|
||||
// CHECK:STDOUT: 'inst+9': {kind: TupleLiteral, arg0: block4, type: type2}
|
||||
// CHECK:STDOUT: 'inst+10': {kind: Converted, arg0: inst+6, arg1: inst+1, type: typeTypeType}
|
||||
// CHECK:STDOUT: 'inst+11': {kind: Converted, arg0: inst+7, arg1: inst+1, type: typeTypeType}
|
||||
@@ -78,7 +64,7 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: 'inst+26': {kind: TupleInit, arg0: empty, arg1: inst+25, type: type1}
|
||||
// CHECK:STDOUT: 'inst+27': {kind: Converted, arg0: inst+19, arg1: inst+26, type: type1}
|
||||
// CHECK:STDOUT: 'inst+28': {kind: TupleInit, arg0: block10, arg1: inst+13, type: type2}
|
||||
// CHECK:STDOUT: 'inst+29': {kind: TupleValue, arg0: block12, type: type2}
|
||||
// CHECK:STDOUT: 'inst+29': {kind: TupleValue, arg0: block11, type: type2}
|
||||
// CHECK:STDOUT: 'inst+30': {kind: Converted, arg0: inst+20, arg1: inst+28, type: type2}
|
||||
// CHECK:STDOUT: 'inst+31': {kind: ReturnExpr, arg0: inst+30, arg1: inst+13}
|
||||
// CHECK:STDOUT: constant_values:
|
||||
@@ -144,11 +130,10 @@ fn Foo(n: ()) -> ((), ()) {
|
||||
// CHECK:STDOUT: block10:
|
||||
// CHECK:STDOUT: 0: inst+24
|
||||
// CHECK:STDOUT: 1: inst+27
|
||||
// CHECK:STDOUT: block11: {}
|
||||
// CHECK:STDOUT: block12:
|
||||
// CHECK:STDOUT: block11:
|
||||
// CHECK:STDOUT: 0: inst+23
|
||||
// CHECK:STDOUT: 1: inst+23
|
||||
// CHECK:STDOUT: block13:
|
||||
// CHECK:STDOUT: block12:
|
||||
// CHECK:STDOUT: 0: inst+0
|
||||
// CHECK:STDOUT: 1: inst+14
|
||||
// CHECK:STDOUT: ...
|
||||
|
||||
+6
-8
@@ -62,9 +62,7 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.5: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.6: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal +Inf [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.9: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.10: f64 = float_literal NaN [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal NaN [template]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
@@ -99,11 +97,11 @@ fn RuntimeCallBadReturnType(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc9_27.2: f64 = converted %float.div.loc9, %.loc9_27.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %b.loc9: f64 = bind_name b, %.loc9_27.2
|
||||
// CHECK:STDOUT: %Div.ref.loc10: Div = name_ref Div, %Div.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc10_18: f64 = float_literal 0 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc10_23: f64 = float_literal 0 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.div.loc10: init f64 = call %Div.ref.loc10(%.loc10_18, %.loc10_23) [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc10_27.1: f64 = value_of_initializer %float.div.loc10 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc10_27.2: f64 = converted %float.div.loc10, %.loc10_27.1 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc10_18: f64 = float_literal 0 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc10_23: f64 = float_literal 0 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %float.div.loc10: init f64 = call %Div.ref.loc10(%.loc10_18, %.loc10_23) [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc10_27.1: f64 = value_of_initializer %float.div.loc10 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc10_27.2: f64 = converted %float.div.loc10, %.loc10_27.1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %c: f64 = bind_name c, %.loc10_27.2
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+12
-14
@@ -42,11 +42,9 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.eq";
|
||||
// CHECK:STDOUT: %struct.2: F = struct_value () [template]
|
||||
// CHECK:STDOUT: %.3: type = ptr_type {} [template]
|
||||
// CHECK:STDOUT: %.4: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.5: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.6: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 2 [template]
|
||||
// CHECK:STDOUT: %.9: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.5: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.6: f64 = float_literal 2 [template]
|
||||
// CHECK:STDOUT: %.7: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %RuntimeCall: type = fn_type @RuntimeCall [template]
|
||||
// CHECK:STDOUT: %struct.3: RuntimeCall = struct_value () [template]
|
||||
// CHECK:STDOUT: }
|
||||
@@ -104,10 +102,10 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.eq";
|
||||
// CHECK:STDOUT: %true_.ref: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %Eq.ref.loc8: Eq = name_ref Eq, file.%Eq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc8_19: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc8_24: f64 = float_literal 1 [template = constants.%.5]
|
||||
// CHECK:STDOUT: %float.eq.loc8: init bool = call %Eq.ref.loc8(%.loc8_19, %.loc8_24) [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc8_13.1: bool = value_of_initializer %float.eq.loc8 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc8_13.2: bool = converted %float.eq.loc8, %.loc8_13.1 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc8_24: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.eq.loc8: init bool = call %Eq.ref.loc8(%.loc8_19, %.loc8_24) [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc8_13.1: bool = value_of_initializer %float.eq.loc8 [template = constants.%.5]
|
||||
// CHECK:STDOUT: %.loc8_13.2: bool = converted %float.eq.loc8, %.loc8_13.1 [template = constants.%.5]
|
||||
// CHECK:STDOUT: if %.loc8_13.2 br !if.expr.then.loc8 else br !if.expr.else.loc8
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc8:
|
||||
@@ -122,11 +120,11 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.eq";
|
||||
// CHECK:STDOUT: %.loc8_13.3: type = block_arg !if.expr.result.loc8 [template = constants.%True]
|
||||
// CHECK:STDOUT: %false_.ref: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %Eq.ref.loc9: Eq = name_ref Eq, file.%Eq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc9_20: f64 = float_literal 1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc9_25: f64 = float_literal 2 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %float.eq.loc9: init bool = call %Eq.ref.loc9(%.loc9_20, %.loc9_25) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_14.1: bool = value_of_initializer %float.eq.loc9 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_14.2: bool = converted %float.eq.loc9, %.loc9_14.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_20: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc9_25: f64 = float_literal 2 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %float.eq.loc9: init bool = call %Eq.ref.loc9(%.loc9_20, %.loc9_25) [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc9_14.1: bool = value_of_initializer %float.eq.loc9 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc9_14.2: bool = converted %float.eq.loc9, %.loc9_14.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: if %.loc9_14.2 br !if.expr.then.loc9 else br !if.expr.else.loc9
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc9:
|
||||
|
||||
+23
-31
@@ -41,17 +41,9 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.4: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.5: f64 = float_literal 2 [template]
|
||||
// CHECK:STDOUT: %.6: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.9: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.10: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.11: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.12: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.13: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %.14: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.15: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.16: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.17: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.8: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.9: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %RuntimeCall: type = fn_type @RuntimeCall [template]
|
||||
// CHECK:STDOUT: %struct.4: RuntimeCall = struct_value () [template]
|
||||
// CHECK:STDOUT: }
|
||||
@@ -135,8 +127,8 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc9_14.3: type = block_arg !if.expr.result.loc9 [template = constants.%False]
|
||||
// CHECK:STDOUT: %false_.ref.loc10: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %Greater.ref.loc10: Greater = name_ref Greater, file.%Greater.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc10_25: f64 = float_literal 1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc10_30: f64 = float_literal 1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc10_25: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc10_30: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.greater.loc10: init bool = call %Greater.ref.loc10(%.loc10_25, %.loc10_30) [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc10_14.1: bool = value_of_initializer %float.greater.loc10 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc10_14.2: bool = converted %float.greater.loc10, %.loc10_14.1 [template = constants.%.6]
|
||||
@@ -154,11 +146,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc10_14.3: type = block_arg !if.expr.result.loc10 [template = constants.%False]
|
||||
// CHECK:STDOUT: %true_.ref.loc11: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %Greater.ref.loc11: Greater = name_ref Greater, file.%Greater.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc11_24: f64 = float_literal 1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc11_29: f64 = float_literal 0 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %float.greater.loc11: init bool = call %Greater.ref.loc11(%.loc11_24, %.loc11_29) [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc11_13.1: bool = value_of_initializer %float.greater.loc11 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc11_13.2: bool = converted %float.greater.loc11, %.loc11_13.1 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc11_24: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc11_29: f64 = float_literal 0 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %float.greater.loc11: init bool = call %Greater.ref.loc11(%.loc11_24, %.loc11_29) [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc11_13.1: bool = value_of_initializer %float.greater.loc11 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc11_13.2: bool = converted %float.greater.loc11, %.loc11_13.1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: if %.loc11_13.2 br !if.expr.then.loc11 else br !if.expr.else.loc11
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc11:
|
||||
@@ -174,11 +166,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %false_.ref.loc12: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %Greater.ref.loc12: Greater = name_ref Greater, file.%Greater.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %Negate.ref.loc12: Negate = name_ref Negate, file.%Negate.decl [template = constants.%struct.2]
|
||||
// CHECK:STDOUT: %.loc12_32: f64 = float_literal 1 [template = constants.%.12]
|
||||
// CHECK:STDOUT: %float.negate.loc12: init f64 = call %Negate.ref.loc12(%.loc12_32) [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_38: f64 = float_literal 0 [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc12_24.1: f64 = value_of_initializer %float.negate.loc12 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_24.2: f64 = converted %float.negate.loc12, %.loc12_24.1 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_32: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.negate.loc12: init f64 = call %Negate.ref.loc12(%.loc12_32) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc12_38: f64 = float_literal 0 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc12_24.1: f64 = value_of_initializer %float.negate.loc12 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc12_24.2: f64 = converted %float.negate.loc12, %.loc12_24.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.greater.loc12: init bool = call %Greater.ref.loc12(%.loc12_24.2, %.loc12_38) [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_14.1: bool = value_of_initializer %float.greater.loc12 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_14.2: bool = converted %float.greater.loc12, %.loc12_14.1 [template = constants.%.6]
|
||||
@@ -196,15 +188,15 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc12_14.3: type = block_arg !if.expr.result.loc12 [template = constants.%False]
|
||||
// CHECK:STDOUT: %true_.ref.loc13: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %Greater.ref.loc13: Greater = name_ref Greater, file.%Greater.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc13_24: f64 = float_literal 0 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc13_24: f64 = float_literal 0 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %Negate.ref.loc13: Negate = name_ref Negate, file.%Negate.decl [template = constants.%struct.2]
|
||||
// CHECK:STDOUT: %.loc13_36: f64 = float_literal 1 [template = constants.%.16]
|
||||
// CHECK:STDOUT: %float.negate.loc13: init f64 = call %Negate.ref.loc13(%.loc13_36) [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_23.1: f64 = value_of_initializer %float.negate.loc13 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_23.2: f64 = converted %float.negate.loc13, %.loc13_23.1 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %float.greater.loc13: init bool = call %Greater.ref.loc13(%.loc13_24, %.loc13_23.2) [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc13_13.1: bool = value_of_initializer %float.greater.loc13 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc13_13.2: bool = converted %float.greater.loc13, %.loc13_13.1 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc13_36: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.negate.loc13: init f64 = call %Negate.ref.loc13(%.loc13_36) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_23.1: f64 = value_of_initializer %float.negate.loc13 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_23.2: f64 = converted %float.negate.loc13, %.loc13_23.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.greater.loc13: init bool = call %Greater.ref.loc13(%.loc13_24, %.loc13_23.2) [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc13_13.1: bool = value_of_initializer %float.greater.loc13 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc13_13.2: bool = converted %float.greater.loc13, %.loc13_13.1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: if %.loc13_13.2 br !if.expr.then.loc13 else br !if.expr.else.loc13
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc13:
|
||||
|
||||
+26
-34
@@ -41,17 +41,9 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.4: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.5: f64 = float_literal 2 [template]
|
||||
// CHECK:STDOUT: %.6: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.9: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.10: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.11: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.12: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.13: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %.14: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.15: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.16: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.17: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %.7: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.9: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %RuntimeCall: type = fn_type @RuntimeCall [template]
|
||||
// CHECK:STDOUT: %struct.4: RuntimeCall = struct_value () [template]
|
||||
// CHECK:STDOUT: }
|
||||
@@ -135,11 +127,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc9_14.3: type = block_arg !if.expr.result.loc9 [template = constants.%False]
|
||||
// CHECK:STDOUT: %true_.ref.loc10: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %GreaterEq.ref.loc10: GreaterEq = name_ref GreaterEq, file.%GreaterEq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc10_26: f64 = float_literal 1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc10_31: f64 = float_literal 1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %float.greater_eq.loc10: init bool = call %GreaterEq.ref.loc10(%.loc10_26, %.loc10_31) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc10_13.1: bool = value_of_initializer %float.greater_eq.loc10 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc10_13.2: bool = converted %float.greater_eq.loc10, %.loc10_13.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc10_26: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc10_31: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.greater_eq.loc10: init bool = call %GreaterEq.ref.loc10(%.loc10_26, %.loc10_31) [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc10_13.1: bool = value_of_initializer %float.greater_eq.loc10 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc10_13.2: bool = converted %float.greater_eq.loc10, %.loc10_13.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: if %.loc10_13.2 br !if.expr.then.loc10 else br !if.expr.else.loc10
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc10:
|
||||
@@ -154,11 +146,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc10_13.3: type = block_arg !if.expr.result.loc10 [template = constants.%True]
|
||||
// CHECK:STDOUT: %true_.ref.loc11: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %GreaterEq.ref.loc11: GreaterEq = name_ref GreaterEq, file.%GreaterEq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc11_26: f64 = float_literal 1 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc11_31: f64 = float_literal 0 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %float.greater_eq.loc11: init bool = call %GreaterEq.ref.loc11(%.loc11_26, %.loc11_31) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc11_13.1: bool = value_of_initializer %float.greater_eq.loc11 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc11_13.2: bool = converted %float.greater_eq.loc11, %.loc11_13.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc11_26: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc11_31: f64 = float_literal 0 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %float.greater_eq.loc11: init bool = call %GreaterEq.ref.loc11(%.loc11_26, %.loc11_31) [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc11_13.1: bool = value_of_initializer %float.greater_eq.loc11 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc11_13.2: bool = converted %float.greater_eq.loc11, %.loc11_13.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: if %.loc11_13.2 br !if.expr.then.loc11 else br !if.expr.else.loc11
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc11:
|
||||
@@ -174,11 +166,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %false_.ref.loc12: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %GreaterEq.ref.loc12: GreaterEq = name_ref GreaterEq, file.%GreaterEq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %Negate.ref.loc12: Negate = name_ref Negate, file.%Negate.decl [template = constants.%struct.2]
|
||||
// CHECK:STDOUT: %.loc12_34: f64 = float_literal 1 [template = constants.%.12]
|
||||
// CHECK:STDOUT: %float.negate.loc12: init f64 = call %Negate.ref.loc12(%.loc12_34) [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_40: f64 = float_literal 0 [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc12_26.1: f64 = value_of_initializer %float.negate.loc12 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_26.2: f64 = converted %float.negate.loc12, %.loc12_26.1 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_34: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.negate.loc12: init f64 = call %Negate.ref.loc12(%.loc12_34) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc12_40: f64 = float_literal 0 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc12_26.1: f64 = value_of_initializer %float.negate.loc12 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc12_26.2: f64 = converted %float.negate.loc12, %.loc12_26.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.greater_eq.loc12: init bool = call %GreaterEq.ref.loc12(%.loc12_26.2, %.loc12_40) [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_14.1: bool = value_of_initializer %float.greater_eq.loc12 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_14.2: bool = converted %float.greater_eq.loc12, %.loc12_14.1 [template = constants.%.6]
|
||||
@@ -196,15 +188,15 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc12_14.3: type = block_arg !if.expr.result.loc12 [template = constants.%False]
|
||||
// CHECK:STDOUT: %true_.ref.loc13: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %GreaterEq.ref.loc13: GreaterEq = name_ref GreaterEq, file.%GreaterEq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc13_26: f64 = float_literal 0 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc13_26: f64 = float_literal 0 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %Negate.ref.loc13: Negate = name_ref Negate, file.%Negate.decl [template = constants.%struct.2]
|
||||
// CHECK:STDOUT: %.loc13_38: f64 = float_literal 1 [template = constants.%.16]
|
||||
// CHECK:STDOUT: %float.negate.loc13: init f64 = call %Negate.ref.loc13(%.loc13_38) [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_25.1: f64 = value_of_initializer %float.negate.loc13 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_25.2: f64 = converted %float.negate.loc13, %.loc13_25.1 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %float.greater_eq.loc13: init bool = call %GreaterEq.ref.loc13(%.loc13_26, %.loc13_25.2) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_13.1: bool = value_of_initializer %float.greater_eq.loc13 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_13.2: bool = converted %float.greater_eq.loc13, %.loc13_13.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_38: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.negate.loc13: init f64 = call %Negate.ref.loc13(%.loc13_38) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_25.1: f64 = value_of_initializer %float.negate.loc13 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_25.2: f64 = converted %float.negate.loc13, %.loc13_25.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.greater_eq.loc13: init bool = call %GreaterEq.ref.loc13(%.loc13_26, %.loc13_25.2) [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc13_13.1: bool = value_of_initializer %float.greater_eq.loc13 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc13_13.2: bool = converted %float.greater_eq.loc13, %.loc13_13.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: if %.loc13_13.2 br !if.expr.then.loc13 else br !if.expr.else.loc13
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc13:
|
||||
|
||||
+26
-34
@@ -41,17 +41,9 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.4: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.5: f64 = float_literal 2 [template]
|
||||
// CHECK:STDOUT: %.6: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.9: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.10: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.11: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.12: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.13: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %.14: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.15: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.16: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.17: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %.7: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.9: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %RuntimeCall: type = fn_type @RuntimeCall [template]
|
||||
// CHECK:STDOUT: %struct.4: RuntimeCall = struct_value () [template]
|
||||
// CHECK:STDOUT: }
|
||||
@@ -135,11 +127,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc9_13.3: type = block_arg !if.expr.result.loc9 [template = constants.%True]
|
||||
// CHECK:STDOUT: %false_.ref.loc10: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %Less.ref.loc10: Less = name_ref Less, file.%Less.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc10_22: f64 = float_literal 1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc10_27: f64 = float_literal 1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %float.less.loc10: init bool = call %Less.ref.loc10(%.loc10_22, %.loc10_27) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc10_14.1: bool = value_of_initializer %float.less.loc10 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc10_14.2: bool = converted %float.less.loc10, %.loc10_14.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc10_22: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc10_27: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.less.loc10: init bool = call %Less.ref.loc10(%.loc10_22, %.loc10_27) [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc10_14.1: bool = value_of_initializer %float.less.loc10 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc10_14.2: bool = converted %float.less.loc10, %.loc10_14.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: if %.loc10_14.2 br !if.expr.then.loc10 else br !if.expr.else.loc10
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc10:
|
||||
@@ -154,11 +146,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc10_14.3: type = block_arg !if.expr.result.loc10 [template = constants.%False]
|
||||
// CHECK:STDOUT: %false_.ref.loc11: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %Less.ref.loc11: Less = name_ref Less, file.%Less.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc11_22: f64 = float_literal 1 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %.loc11_27: f64 = float_literal 0 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %float.less.loc11: init bool = call %Less.ref.loc11(%.loc11_22, %.loc11_27) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc11_14.1: bool = value_of_initializer %float.less.loc11 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc11_14.2: bool = converted %float.less.loc11, %.loc11_14.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc11_22: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc11_27: f64 = float_literal 0 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %float.less.loc11: init bool = call %Less.ref.loc11(%.loc11_22, %.loc11_27) [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc11_14.1: bool = value_of_initializer %float.less.loc11 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc11_14.2: bool = converted %float.less.loc11, %.loc11_14.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: if %.loc11_14.2 br !if.expr.then.loc11 else br !if.expr.else.loc11
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc11:
|
||||
@@ -174,11 +166,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %true_.ref.loc12: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %Less.ref.loc12: Less = name_ref Less, file.%Less.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %Negate.ref.loc12: Negate = name_ref Negate, file.%Negate.decl [template = constants.%struct.2]
|
||||
// CHECK:STDOUT: %.loc12_28: f64 = float_literal 1 [template = constants.%.12]
|
||||
// CHECK:STDOUT: %float.negate.loc12: init f64 = call %Negate.ref.loc12(%.loc12_28) [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_34: f64 = float_literal 0 [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc12_20.1: f64 = value_of_initializer %float.negate.loc12 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_20.2: f64 = converted %float.negate.loc12, %.loc12_20.1 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_28: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.negate.loc12: init f64 = call %Negate.ref.loc12(%.loc12_28) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc12_34: f64 = float_literal 0 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc12_20.1: f64 = value_of_initializer %float.negate.loc12 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc12_20.2: f64 = converted %float.negate.loc12, %.loc12_20.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.less.loc12: init bool = call %Less.ref.loc12(%.loc12_20.2, %.loc12_34) [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_13.1: bool = value_of_initializer %float.less.loc12 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_13.2: bool = converted %float.less.loc12, %.loc12_13.1 [template = constants.%.6]
|
||||
@@ -196,15 +188,15 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc12_13.3: type = block_arg !if.expr.result.loc12 [template = constants.%True]
|
||||
// CHECK:STDOUT: %false_.ref.loc13: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %Less.ref.loc13: Less = name_ref Less, file.%Less.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc13_22: f64 = float_literal 0 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc13_22: f64 = float_literal 0 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %Negate.ref.loc13: Negate = name_ref Negate, file.%Negate.decl [template = constants.%struct.2]
|
||||
// CHECK:STDOUT: %.loc13_34: f64 = float_literal 1 [template = constants.%.16]
|
||||
// CHECK:STDOUT: %float.negate.loc13: init f64 = call %Negate.ref.loc13(%.loc13_34) [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_21.1: f64 = value_of_initializer %float.negate.loc13 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_21.2: f64 = converted %float.negate.loc13, %.loc13_21.1 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %float.less.loc13: init bool = call %Less.ref.loc13(%.loc13_22, %.loc13_21.2) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_14.1: bool = value_of_initializer %float.less.loc13 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_14.2: bool = converted %float.less.loc13, %.loc13_14.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_34: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.negate.loc13: init f64 = call %Negate.ref.loc13(%.loc13_34) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_21.1: f64 = value_of_initializer %float.negate.loc13 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_21.2: f64 = converted %float.negate.loc13, %.loc13_21.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.less.loc13: init bool = call %Less.ref.loc13(%.loc13_22, %.loc13_21.2) [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc13_14.1: bool = value_of_initializer %float.less.loc13 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc13_14.2: bool = converted %float.less.loc13, %.loc13_14.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: if %.loc13_14.2 br !if.expr.then.loc13 else br !if.expr.else.loc13
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc13:
|
||||
|
||||
+23
-31
@@ -41,17 +41,9 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.4: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.5: f64 = float_literal 2 [template]
|
||||
// CHECK:STDOUT: %.6: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.9: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.10: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.11: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.12: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.13: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %.14: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.15: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.16: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.17: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal 0 [template]
|
||||
// CHECK:STDOUT: %.8: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.9: f64 = float_literal -1 [template]
|
||||
// CHECK:STDOUT: %RuntimeCall: type = fn_type @RuntimeCall [template]
|
||||
// CHECK:STDOUT: %struct.4: RuntimeCall = struct_value () [template]
|
||||
// CHECK:STDOUT: }
|
||||
@@ -135,8 +127,8 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc9_13.3: type = block_arg !if.expr.result.loc9 [template = constants.%True]
|
||||
// CHECK:STDOUT: %true_.ref.loc10: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %LessEq.ref.loc10: LessEq = name_ref LessEq, file.%LessEq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc10_23: f64 = float_literal 1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc10_28: f64 = float_literal 1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc10_23: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc10_28: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.less_eq.loc10: init bool = call %LessEq.ref.loc10(%.loc10_23, %.loc10_28) [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc10_13.1: bool = value_of_initializer %float.less_eq.loc10 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc10_13.2: bool = converted %float.less_eq.loc10, %.loc10_13.1 [template = constants.%.6]
|
||||
@@ -154,11 +146,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc10_13.3: type = block_arg !if.expr.result.loc10 [template = constants.%True]
|
||||
// CHECK:STDOUT: %false_.ref.loc11: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %LessEq.ref.loc11: LessEq = name_ref LessEq, file.%LessEq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc11_24: f64 = float_literal 1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc11_29: f64 = float_literal 0 [template = constants.%.10]
|
||||
// CHECK:STDOUT: %float.less_eq.loc11: init bool = call %LessEq.ref.loc11(%.loc11_24, %.loc11_29) [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc11_14.1: bool = value_of_initializer %float.less_eq.loc11 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc11_14.2: bool = converted %float.less_eq.loc11, %.loc11_14.1 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc11_24: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc11_29: f64 = float_literal 0 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %float.less_eq.loc11: init bool = call %LessEq.ref.loc11(%.loc11_24, %.loc11_29) [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc11_14.1: bool = value_of_initializer %float.less_eq.loc11 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc11_14.2: bool = converted %float.less_eq.loc11, %.loc11_14.1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: if %.loc11_14.2 br !if.expr.then.loc11 else br !if.expr.else.loc11
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc11:
|
||||
@@ -174,11 +166,11 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %true_.ref.loc12: True = name_ref true_, %true_
|
||||
// CHECK:STDOUT: %LessEq.ref.loc12: LessEq = name_ref LessEq, file.%LessEq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %Negate.ref.loc12: Negate = name_ref Negate, file.%Negate.decl [template = constants.%struct.2]
|
||||
// CHECK:STDOUT: %.loc12_30: f64 = float_literal 1 [template = constants.%.12]
|
||||
// CHECK:STDOUT: %float.negate.loc12: init f64 = call %Negate.ref.loc12(%.loc12_30) [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_36: f64 = float_literal 0 [template = constants.%.14]
|
||||
// CHECK:STDOUT: %.loc12_22.1: f64 = value_of_initializer %float.negate.loc12 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_22.2: f64 = converted %float.negate.loc12, %.loc12_22.1 [template = constants.%.13]
|
||||
// CHECK:STDOUT: %.loc12_30: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.negate.loc12: init f64 = call %Negate.ref.loc12(%.loc12_30) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc12_36: f64 = float_literal 0 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc12_22.1: f64 = value_of_initializer %float.negate.loc12 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc12_22.2: f64 = converted %float.negate.loc12, %.loc12_22.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.less_eq.loc12: init bool = call %LessEq.ref.loc12(%.loc12_22.2, %.loc12_36) [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_13.1: bool = value_of_initializer %float.less_eq.loc12 [template = constants.%.6]
|
||||
// CHECK:STDOUT: %.loc12_13.2: bool = converted %float.less_eq.loc12, %.loc12_13.1 [template = constants.%.6]
|
||||
@@ -196,15 +188,15 @@ fn RuntimeCall(a: f64, b: f64) -> bool {
|
||||
// CHECK:STDOUT: %.loc12_13.3: type = block_arg !if.expr.result.loc12 [template = constants.%True]
|
||||
// CHECK:STDOUT: %false_.ref.loc13: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %LessEq.ref.loc13: LessEq = name_ref LessEq, file.%LessEq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc13_24: f64 = float_literal 0 [template = constants.%.15]
|
||||
// CHECK:STDOUT: %.loc13_24: f64 = float_literal 0 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %Negate.ref.loc13: Negate = name_ref Negate, file.%Negate.decl [template = constants.%struct.2]
|
||||
// CHECK:STDOUT: %.loc13_36: f64 = float_literal 1 [template = constants.%.16]
|
||||
// CHECK:STDOUT: %float.negate.loc13: init f64 = call %Negate.ref.loc13(%.loc13_36) [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_23.1: f64 = value_of_initializer %float.negate.loc13 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %.loc13_23.2: f64 = converted %float.negate.loc13, %.loc13_23.1 [template = constants.%.17]
|
||||
// CHECK:STDOUT: %float.less_eq.loc13: init bool = call %LessEq.ref.loc13(%.loc13_24, %.loc13_23.2) [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc13_14.1: bool = value_of_initializer %float.less_eq.loc13 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc13_14.2: bool = converted %float.less_eq.loc13, %.loc13_14.1 [template = constants.%.11]
|
||||
// CHECK:STDOUT: %.loc13_36: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.negate.loc13: init f64 = call %Negate.ref.loc13(%.loc13_36) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_23.1: f64 = value_of_initializer %float.negate.loc13 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc13_23.2: f64 = converted %float.negate.loc13, %.loc13_23.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %float.less_eq.loc13: init bool = call %LessEq.ref.loc13(%.loc13_24, %.loc13_23.2) [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc13_14.1: bool = value_of_initializer %float.less_eq.loc13 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %.loc13_14.2: bool = converted %float.less_eq.loc13, %.loc13_14.1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: if %.loc13_14.2 br !if.expr.then.loc13 else br !if.expr.else.loc13
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc13:
|
||||
|
||||
+6
-8
@@ -44,9 +44,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.neq";
|
||||
// CHECK:STDOUT: %.4: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.5: f64 = float_literal 2 [template]
|
||||
// CHECK:STDOUT: %.6: bool = bool_literal true [template]
|
||||
// CHECK:STDOUT: %.7: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.8: f64 = float_literal 1 [template]
|
||||
// CHECK:STDOUT: %.9: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %.7: bool = bool_literal false [template]
|
||||
// CHECK:STDOUT: %RuntimeCall: type = fn_type @RuntimeCall [template]
|
||||
// CHECK:STDOUT: %struct.3: RuntimeCall = struct_value () [template]
|
||||
// CHECK:STDOUT: }
|
||||
@@ -122,11 +120,11 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.neq";
|
||||
// CHECK:STDOUT: %.loc8_13.3: type = block_arg !if.expr.result.loc8 [template = constants.%True]
|
||||
// CHECK:STDOUT: %false_.ref: False = name_ref false_, %false_
|
||||
// CHECK:STDOUT: %Neq.ref.loc9: Neq = name_ref Neq, file.%Neq.decl [template = constants.%struct.1]
|
||||
// CHECK:STDOUT: %.loc9_21: f64 = float_literal 1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc9_26: f64 = float_literal 1 [template = constants.%.8]
|
||||
// CHECK:STDOUT: %float.neq.loc9: init bool = call %Neq.ref.loc9(%.loc9_21, %.loc9_26) [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_14.1: bool = value_of_initializer %float.neq.loc9 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_14.2: bool = converted %float.neq.loc9, %.loc9_14.1 [template = constants.%.9]
|
||||
// CHECK:STDOUT: %.loc9_21: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %.loc9_26: f64 = float_literal 1 [template = constants.%.4]
|
||||
// CHECK:STDOUT: %float.neq.loc9: init bool = call %Neq.ref.loc9(%.loc9_21, %.loc9_26) [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc9_14.1: bool = value_of_initializer %float.neq.loc9 [template = constants.%.7]
|
||||
// CHECK:STDOUT: %.loc9_14.2: bool = converted %float.neq.loc9, %.loc9_14.1 [template = constants.%.7]
|
||||
// CHECK:STDOUT: if %.loc9_14.2 br !if.expr.then.loc9 else br !if.expr.else.loc9
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then.loc9:
|
||||
|
||||
+2
-5
@@ -21,11 +21,8 @@ var str2: String = "ab'\"c";
|
||||
// CHECK:STDOUT: ints:
|
||||
// CHECK:STDOUT: int0: 32
|
||||
// CHECK:STDOUT: int1: 1
|
||||
// CHECK:STDOUT: int2: 32
|
||||
// CHECK:STDOUT: int3: 8
|
||||
// CHECK:STDOUT: int4: 64
|
||||
// CHECK:STDOUT: int5: 64
|
||||
// CHECK:STDOUT: int6: 64
|
||||
// CHECK:STDOUT: int2: 8
|
||||
// CHECK:STDOUT: int3: 64
|
||||
// CHECK:STDOUT: reals:
|
||||
// CHECK:STDOUT: real0: 10*10^-1
|
||||
// CHECK:STDOUT: real1: 8*10^7
|
||||
|
||||
@@ -18,6 +18,7 @@ cc_library(
|
||||
name = "block_value_store",
|
||||
hdrs = ["block_value_store.h"],
|
||||
deps = [
|
||||
"//common:hashing",
|
||||
"//toolchain/base:value_store",
|
||||
"//toolchain/base:yaml",
|
||||
"@llvm-project//llvm:Support",
|
||||
@@ -68,6 +69,7 @@ cc_library(
|
||||
":ids",
|
||||
":inst_kind",
|
||||
"//common:check",
|
||||
"//common:hashing",
|
||||
"//common:ostream",
|
||||
"//common:struct_reflection",
|
||||
"//toolchain/base:index_base",
|
||||
@@ -82,11 +84,10 @@ cc_library(
|
||||
"constant.cpp",
|
||||
"file.cpp",
|
||||
"function.cpp",
|
||||
"inst_profile.cpp",
|
||||
"inst_profile.h",
|
||||
"name.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"bind_name.h",
|
||||
"builtin_function_kind.h",
|
||||
"class.h",
|
||||
"constant.h",
|
||||
@@ -113,6 +114,7 @@ cc_library(
|
||||
"//common:check",
|
||||
"//common:enum_base",
|
||||
"//common:error",
|
||||
"//common:hashing",
|
||||
"//toolchain/base:kind_switch",
|
||||
"//toolchain/base:value_store",
|
||||
"//toolchain/base:yaml",
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_SEM_IR_BIND_NAME_H_
|
||||
#define CARBON_TOOLCHAIN_SEM_IR_BIND_NAME_H_
|
||||
|
||||
#include "common/hashing.h"
|
||||
#include "toolchain/base/value_store.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
struct BindNameInfo : public Printable<BindNameInfo> {
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
out << "{name: " << name_id << ", enclosing_scope: " << enclosing_scope_id
|
||||
<< ", index: " << bind_index << "}";
|
||||
}
|
||||
|
||||
// The name.
|
||||
NameId name_id;
|
||||
// The enclosing scope.
|
||||
NameScopeId enclosing_scope_id;
|
||||
// The index for a compile-time binding. Invalid for a runtime binding.
|
||||
CompileTimeBindIndex bind_index;
|
||||
};
|
||||
|
||||
// Hashing for BindNameInfo.
|
||||
inline auto CarbonHashValue(const BindNameInfo& value, uint64_t seed)
|
||||
-> HashCode {
|
||||
Hasher hasher(seed);
|
||||
hasher.Hash(value);
|
||||
return static_cast<HashCode>(hasher);
|
||||
}
|
||||
|
||||
// DenseMapInfo for BindNameInfo.
|
||||
struct BindNameInfoDenseMapInfo {
|
||||
static auto getEmptyKey() -> BindNameInfo {
|
||||
return BindNameInfo{.name_id = NameId::Invalid,
|
||||
.enclosing_scope_id = NameScopeId::Invalid,
|
||||
.bind_index = CompileTimeBindIndex(
|
||||
CompileTimeBindIndex::InvalidIndex - 1)};
|
||||
}
|
||||
static auto getTombstoneKey() -> BindNameInfo {
|
||||
return BindNameInfo{.name_id = NameId::Invalid,
|
||||
.enclosing_scope_id = NameScopeId::Invalid,
|
||||
.bind_index = CompileTimeBindIndex(
|
||||
CompileTimeBindIndex::InvalidIndex - 2)};
|
||||
}
|
||||
static auto getHashValue(const BindNameInfo& val) -> unsigned {
|
||||
return static_cast<uint64_t>(HashValue(val));
|
||||
}
|
||||
static auto isEqual(const BindNameInfo& lhs, const BindNameInfo& rhs)
|
||||
-> bool {
|
||||
return std::memcmp(&lhs, &rhs, sizeof(BindNameInfo)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Value store for BindNameInfo. In addition to the regular ValueStore
|
||||
// functionality, this can provide optional canonical IDs for BindNameInfos.
|
||||
struct BindNameStore : public ValueStore<BindNameId> {
|
||||
public:
|
||||
// Convert an ID to a canonical ID. All calls to this with equivalent
|
||||
// `BindNameInfo`s will return the same `BindNameId`.
|
||||
auto MakeCanonical(BindNameId id) -> BindNameId {
|
||||
return canonical_ids_.insert({Get(id), id}).first->second;
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::DenseMap<BindNameInfo, BindNameId, BindNameInfoDenseMapInfo>
|
||||
canonical_ids_;
|
||||
};
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_SEM_IR_BIND_NAME_H_
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/hashing.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "toolchain/base/value_store.h"
|
||||
#include "toolchain/base/yaml.h"
|
||||
@@ -47,6 +48,25 @@ class BlockValueStore : public Yaml::Printable<BlockValueStore<IdT>> {
|
||||
return values_.Get(id);
|
||||
}
|
||||
|
||||
// Adds a block or finds an existing canonical block with the given content,
|
||||
// and returns an ID to reference it.
|
||||
auto AddCanonical(llvm::ArrayRef<ElementType> content) -> IdT {
|
||||
auto [it, added] = canonical_blocks_.insert({{content}, IdT::Invalid});
|
||||
if (added) {
|
||||
auto id = Add(content);
|
||||
it->first.data = Get(id);
|
||||
it->second = id;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Promotes an existing block ID to a canonical block ID, or returns an
|
||||
// existing canonical block ID if the block was already added. The specified
|
||||
// block must not be modified after this point.
|
||||
auto MakeCanonical(IdT id) -> IdT {
|
||||
return canonical_blocks_.insert({{Get(id)}, id}).first->second;
|
||||
}
|
||||
|
||||
auto OutputYaml() const -> Yaml::OutputMapping {
|
||||
return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
|
||||
for (auto block_index : llvm::seq(values_.size())) {
|
||||
@@ -82,6 +102,41 @@ class BlockValueStore : public Yaml::Printable<BlockValueStore<IdT>> {
|
||||
}
|
||||
|
||||
private:
|
||||
// A canonical block, for which we allocate a deduplicated ID.
|
||||
struct CanonicalBlock {
|
||||
// This is mutable so we can repoint it at the allocated data if insertion
|
||||
// succeeds.
|
||||
mutable llvm::ArrayRef<ElementType> data;
|
||||
|
||||
// See common/hashing.h.
|
||||
friend auto CarbonHashValue(CanonicalBlock block, uint64_t seed)
|
||||
-> HashCode {
|
||||
Hasher hasher(seed);
|
||||
hasher.HashSizedBytes(block.data);
|
||||
return static_cast<HashCode>(hasher);
|
||||
}
|
||||
};
|
||||
|
||||
struct CanonicalBlockDenseMapInfo {
|
||||
// Blocks whose data() points to the start of `SpecialData` are used to
|
||||
// represent the special "empty" and "tombstone" states.
|
||||
static constexpr ElementType SpecialData[1] = {ElementType::Invalid};
|
||||
static auto getEmptyKey() -> CanonicalBlock {
|
||||
return CanonicalBlock{
|
||||
llvm::ArrayRef(SpecialData, static_cast<size_t>(0))};
|
||||
}
|
||||
static auto getTombstoneKey() -> CanonicalBlock {
|
||||
return CanonicalBlock{llvm::ArrayRef(SpecialData, 1)};
|
||||
}
|
||||
static auto getHashValue(CanonicalBlock val) -> unsigned {
|
||||
return static_cast<uint64_t>(HashValue(val));
|
||||
}
|
||||
static auto isEqual(CanonicalBlock lhs, CanonicalBlock rhs) -> bool {
|
||||
return lhs.data == rhs.data && (lhs.data.data() == SpecialData) ==
|
||||
(rhs.data.data() == SpecialData);
|
||||
}
|
||||
};
|
||||
|
||||
// Allocates an uninitialized array using our slab allocator.
|
||||
auto AllocateUninitialized(std::size_t size)
|
||||
-> llvm::MutableArrayRef<ElementType> {
|
||||
@@ -103,6 +158,8 @@ class BlockValueStore : public Yaml::Printable<BlockValueStore<IdT>> {
|
||||
|
||||
llvm::BumpPtrAllocator* allocator_;
|
||||
ValueStore<IdT> values_;
|
||||
llvm::DenseMap<CanonicalBlock, IdT, CanonicalBlockDenseMapInfo>
|
||||
canonical_blocks_;
|
||||
};
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
@@ -4,73 +4,25 @@
|
||||
|
||||
#include "toolchain/sem_ir/constant.h"
|
||||
|
||||
#include "toolchain/sem_ir/inst_profile.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
auto ConstantStore::GetOrAdd(Inst inst, bool is_symbolic) -> ConstantId {
|
||||
// Check that we're allowed to form this kind of constant.
|
||||
switch (inst.kind().constant_kind()) {
|
||||
case InstConstantKind::Never:
|
||||
CARBON_FATAL() << "Should not form a constant from instruction " << inst;
|
||||
break;
|
||||
case InstConstantKind::SymbolicOnly:
|
||||
CARBON_CHECK(is_symbolic)
|
||||
<< "Should only form a symbolic constant from instruction " << inst;
|
||||
break;
|
||||
case InstConstantKind::Conditional:
|
||||
break;
|
||||
case InstConstantKind::Always:
|
||||
CARBON_CHECK(!is_symbolic)
|
||||
<< "Should only form a template constant from instruction " << inst;
|
||||
break;
|
||||
auto [it, added] = map_.insert({inst, ConstantId::Invalid});
|
||||
if (added) {
|
||||
auto inst_id = sem_ir_.insts().AddInNoBlock(LocIdAndInst::NoLoc(inst));
|
||||
auto const_id = is_symbolic
|
||||
? SemIR::ConstantId::ForSymbolicConstant(inst_id)
|
||||
: SemIR::ConstantId::ForTemplateConstant(inst_id);
|
||||
it->second = const_id;
|
||||
sem_ir_.constant_values().Set(inst_id, const_id);
|
||||
constants_.push_back(inst_id);
|
||||
} else {
|
||||
CARBON_CHECK(it->second != ConstantId::Invalid);
|
||||
CARBON_CHECK(it->second.is_symbolic() == is_symbolic);
|
||||
}
|
||||
|
||||
// Compute the instruction's profile.
|
||||
ConstantNode node = {.inst = inst, .constant_id = ConstantId::NotConstant};
|
||||
llvm::FoldingSetNodeID id;
|
||||
node.Profile(id, constants_.getContext());
|
||||
|
||||
// Check if we have already created this constant.
|
||||
void* insert_pos;
|
||||
if (ConstantNode* found = constants_.FindNodeOrInsertPos(id, insert_pos)) {
|
||||
CARBON_CHECK(found->constant_id.is_constant())
|
||||
<< "Found non-constant in constant store for " << inst;
|
||||
CARBON_CHECK(found->constant_id.is_symbolic() == is_symbolic)
|
||||
<< "Mismatch in phase for constant " << inst;
|
||||
return found->constant_id;
|
||||
}
|
||||
|
||||
// Create the new inst and insert the new node.
|
||||
auto inst_id =
|
||||
constants_.getContext()->insts().AddInNoBlock(LocIdAndInst::NoLoc(inst));
|
||||
auto constant_id = is_symbolic
|
||||
? SemIR::ConstantId::ForSymbolicConstant(inst_id)
|
||||
: SemIR::ConstantId::ForTemplateConstant(inst_id);
|
||||
node.constant_id = constant_id;
|
||||
constants_.InsertNode(new (*allocator_) ConstantNode(node), insert_pos);
|
||||
|
||||
// The constant value of any constant instruction is that instruction itself.
|
||||
constants_.getContext()->constant_values().Set(inst_id, constant_id);
|
||||
return constant_id;
|
||||
}
|
||||
|
||||
auto ConstantStore::GetAsVector() const -> llvm::SmallVector<InstId, 0> {
|
||||
llvm::SmallVector<InstId, 0> result;
|
||||
result.reserve(constants_.size());
|
||||
for (const ConstantNode& node : constants_) {
|
||||
result.push_back(node.constant_id.inst_id());
|
||||
}
|
||||
// For stability, put the results into index order. This happens to also be
|
||||
// insertion order.
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](InstId a, InstId b) { return a.index < b.index; });
|
||||
return result;
|
||||
}
|
||||
|
||||
auto ConstantStore::ConstantNode::Profile(llvm::FoldingSetNodeID& id,
|
||||
File* sem_ir) -> void {
|
||||
ProfileConstant(id, *sem_ir, inst);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
@@ -57,8 +57,8 @@ class ConstantValueStore {
|
||||
// Provides storage for instructions representing deduplicated global constants.
|
||||
class ConstantStore {
|
||||
public:
|
||||
explicit ConstantStore(File& sem_ir, llvm::BumpPtrAllocator& allocator)
|
||||
: allocator_(&allocator), constants_(&sem_ir) {}
|
||||
explicit ConstantStore(File& sem_ir, llvm::BumpPtrAllocator& /*allocator*/)
|
||||
: sem_ir_(sem_ir) {}
|
||||
|
||||
// Adds a new constant instruction, or gets the existing constant with this
|
||||
// value. Returns the ID of the constant.
|
||||
@@ -69,25 +69,14 @@ class ConstantStore {
|
||||
|
||||
// Returns a copy of the constant IDs as a vector, in an arbitrary but
|
||||
// stable order. This should not be used anywhere performance-sensitive.
|
||||
auto GetAsVector() const -> llvm::SmallVector<InstId, 0>;
|
||||
auto array_ref() const -> llvm::ArrayRef<InstId> { return constants_; }
|
||||
|
||||
auto size() const -> int { return constants_.size(); }
|
||||
|
||||
private:
|
||||
// TODO: We store two copies of each constant instruction: one in insts() and
|
||||
// one here. We could avoid one of those copies and store just an InstId here,
|
||||
// at the cost of some more indirection when recomputing profiles during
|
||||
// lookup. Once we have a representative data set, we should measure the
|
||||
// impact on compile time from that change.
|
||||
struct ConstantNode : llvm::FoldingSetNode {
|
||||
Inst inst;
|
||||
ConstantId constant_id;
|
||||
|
||||
auto Profile(llvm::FoldingSetNodeID& id, File* sem_ir) -> void;
|
||||
};
|
||||
|
||||
llvm::BumpPtrAllocator* allocator_;
|
||||
llvm::ContextualFoldingSet<ConstantNode, File*> constants_;
|
||||
File& sem_ir_;
|
||||
llvm::DenseMap<Inst, ConstantId> map_;
|
||||
llvm::SmallVector<InstId, 0> constants_;
|
||||
};
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
@@ -43,6 +43,12 @@ class CopyOnWriteBlock {
|
||||
// called once all modifications have been performed.
|
||||
auto id() const -> BlockIdType { return id_; }
|
||||
|
||||
// Gets a canonical block ID containing the resulting elements. This assumes
|
||||
// the original block ID, if specified, was also canonical.
|
||||
auto GetCanonical() const -> BlockIdType {
|
||||
return id_ == source_id_ ? id_ : (file_.*ValueStore)().MakeCanonical(id_);
|
||||
}
|
||||
|
||||
// Sets the element at index `i` within the block. Lazily allocates a new
|
||||
// block when the value changes for the first time.
|
||||
auto Set(int i, typename BlockIdType::ElementType value) -> void {
|
||||
|
||||
+8
-25
@@ -12,6 +12,7 @@
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "toolchain/base/value_store.h"
|
||||
#include "toolchain/base/yaml.h"
|
||||
#include "toolchain/sem_ir/bind_name.h"
|
||||
#include "toolchain/sem_ir/class.h"
|
||||
#include "toolchain/sem_ir/constant.h"
|
||||
#include "toolchain/sem_ir/function.h"
|
||||
@@ -27,22 +28,6 @@
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
struct BindNameInfo : public Printable<BindNameInfo> {
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
out << "{name: " << name_id << ", enclosing_scope: " << enclosing_scope_id
|
||||
<< ", index: " << bind_index << "}";
|
||||
}
|
||||
|
||||
// The name.
|
||||
NameId name_id;
|
||||
// The enclosing scope.
|
||||
NameScopeId enclosing_scope_id;
|
||||
// The index for a compile-time binding. Invalid for a runtime binding.
|
||||
CompileTimeBindIndex bind_index;
|
||||
};
|
||||
|
||||
class File;
|
||||
|
||||
// Provides semantic analysis on a Parse::Tree.
|
||||
class File : public Printable<File> {
|
||||
public:
|
||||
@@ -108,16 +93,16 @@ class File : public Printable<File> {
|
||||
auto identifiers() const -> const StringStoreWrapper<IdentifierId>& {
|
||||
return value_stores_->identifiers();
|
||||
}
|
||||
auto ints() -> ValueStore<IntId>& { return value_stores_->ints(); }
|
||||
auto ints() const -> const ValueStore<IntId>& {
|
||||
auto ints() -> CanonicalValueStore<IntId>& { return value_stores_->ints(); }
|
||||
auto ints() const -> const CanonicalValueStore<IntId>& {
|
||||
return value_stores_->ints();
|
||||
}
|
||||
auto reals() -> ValueStore<RealId>& { return value_stores_->reals(); }
|
||||
auto reals() const -> const ValueStore<RealId>& {
|
||||
return value_stores_->reals();
|
||||
}
|
||||
auto floats() -> ValueStore<FloatId>& { return value_stores_->floats(); }
|
||||
auto floats() const -> const ValueStore<FloatId>& {
|
||||
auto floats() -> FloatValueStore& { return value_stores_->floats(); }
|
||||
auto floats() const -> const FloatValueStore& {
|
||||
return value_stores_->floats();
|
||||
}
|
||||
auto string_literal_values() -> StringStoreWrapper<StringLiteralValueId>& {
|
||||
@@ -128,10 +113,8 @@ class File : public Printable<File> {
|
||||
return value_stores_->string_literal_values();
|
||||
}
|
||||
|
||||
auto bind_names() -> ValueStore<BindNameId>& { return bind_names_; }
|
||||
auto bind_names() const -> const ValueStore<BindNameId>& {
|
||||
return bind_names_;
|
||||
}
|
||||
auto bind_names() -> BindNameStore& { return bind_names_; }
|
||||
auto bind_names() const -> const BindNameStore& { return bind_names_; }
|
||||
auto functions() -> ValueStore<FunctionId>& { return functions_; }
|
||||
auto functions() const -> const ValueStore<FunctionId>& { return functions_; }
|
||||
auto classes() -> ValueStore<ClassId>& { return classes_; }
|
||||
@@ -208,7 +191,7 @@ class File : public Printable<File> {
|
||||
std::string filename_;
|
||||
|
||||
// Storage for bind names.
|
||||
ValueStore<BindNameId> bind_names_;
|
||||
BindNameStore bind_names_;
|
||||
|
||||
// Storage for callable objects.
|
||||
ValueStore<FunctionId> functions_;
|
||||
|
||||
@@ -127,7 +127,7 @@ class Formatter {
|
||||
llvm::SaveAndRestore constants_scope(scope_, InstNamer::ScopeId::Constants);
|
||||
out_ << inst_namer_.GetScopeName(InstNamer::ScopeId::Constants) << " ";
|
||||
OpenBrace();
|
||||
FormatCodeBlock(sem_ir_.constants().GetAsVector());
|
||||
FormatCodeBlock(sem_ir_.constants().array_ref());
|
||||
CloseBrace();
|
||||
out_ << "\n\n";
|
||||
}
|
||||
|
||||
@@ -203,6 +203,8 @@ struct CompileTimeBindIndex : public IndexBase,
|
||||
|
||||
constexpr CompileTimeBindIndex CompileTimeBindIndex::Invalid =
|
||||
CompileTimeBindIndex(InvalidIndex);
|
||||
// Note that InvalidIndex - 1 and InvalidIndex - 2 are used by
|
||||
// DenseMapInfo<BindNameInfo>.
|
||||
|
||||
// The ID of a function.
|
||||
struct FunctionId : public IdBase, public Printable<FunctionId> {
|
||||
@@ -538,6 +540,9 @@ struct TypeBlockId : public IdBase, public Printable<TypeBlockId> {
|
||||
using ElementType = TypeId;
|
||||
using ValueType = llvm::MutableArrayRef<ElementType>;
|
||||
|
||||
// An explicitly invalid ID.
|
||||
static const TypeBlockId Invalid;
|
||||
|
||||
using IdBase::IdBase;
|
||||
auto Print(llvm::raw_ostream& out) const -> void {
|
||||
out << "typeBlock";
|
||||
@@ -545,6 +550,8 @@ struct TypeBlockId : public IdBase, public Printable<TypeBlockId> {
|
||||
}
|
||||
};
|
||||
|
||||
constexpr TypeBlockId TypeBlockId::Invalid = TypeBlockId(InvalidIndex);
|
||||
|
||||
// An index for element access, for structs, tuples, and classes.
|
||||
struct ElementIndex : public IndexBase, public Printable<ElementIndex> {
|
||||
using IndexBase::IndexBase;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
auto Inst::Print(llvm::raw_ostream& out) const -> void {
|
||||
out << "{kind: " << kind_;
|
||||
out << "{kind: " << kind();
|
||||
|
||||
auto print_args = [&](auto info) {
|
||||
using Info = decltype(info);
|
||||
@@ -19,7 +19,7 @@ auto Inst::Print(llvm::raw_ostream& out) const -> void {
|
||||
}
|
||||
};
|
||||
|
||||
switch (kind_) {
|
||||
switch (kind()) {
|
||||
#define CARBON_SEM_IR_INST_KIND(Name) \
|
||||
case Name::Kind: \
|
||||
print_args(Internal::InstLikeTypeInfo<Name>()); \
|
||||
|
||||
+41
-7
@@ -9,6 +9,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/hashing.h"
|
||||
#include "common/ostream.h"
|
||||
#include "common/struct_reflection.h"
|
||||
#include "toolchain/base/index_base.h"
|
||||
@@ -129,15 +130,17 @@ class Inst : public Printable<Inst> {
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
Inst(TypedInst typed_inst)
|
||||
// kind_ is always overwritten below.
|
||||
: kind_(InstKind::Make({})),
|
||||
: kind_(),
|
||||
type_id_(TypeId::Invalid),
|
||||
arg0_(InstId::InvalidIndex),
|
||||
arg1_(InstId::InvalidIndex) {
|
||||
if constexpr (Internal::HasKindMemberAsField<TypedInst>) {
|
||||
kind_ = typed_inst.kind;
|
||||
kind_ = typed_inst.kind.AsInt();
|
||||
} else {
|
||||
kind_ = TypedInst::Kind;
|
||||
kind_ = TypedInst::Kind.AsInt();
|
||||
}
|
||||
CARBON_CHECK(kind_ >= 0)
|
||||
<< "Negative kind values are reserved for DenseMapInfo.";
|
||||
if constexpr (Internal::HasTypeIdMember<TypedInst>) {
|
||||
type_id_ = typed_inst.type_id;
|
||||
}
|
||||
@@ -205,7 +208,9 @@ class Inst : public Printable<Inst> {
|
||||
}
|
||||
}
|
||||
|
||||
auto kind() const -> InstKind { return kind_; }
|
||||
auto kind() const -> InstKind {
|
||||
return InstKind::Make(static_cast<InstKind::RawEnumType>(kind_));
|
||||
}
|
||||
|
||||
// Gets the type of the value produced by evaluating this instruction.
|
||||
auto type_id() const -> TypeId { return type_id_; }
|
||||
@@ -220,7 +225,9 @@ class Inst : public Printable<Inst> {
|
||||
}
|
||||
|
||||
// Gets the kinds of IDs used for arg0 and arg1 of this instruction.
|
||||
auto ArgKinds() const -> std::pair<IdKind, IdKind> { return ArgKinds(kind_); }
|
||||
auto ArgKinds() const -> std::pair<IdKind, IdKind> {
|
||||
return ArgKinds(kind());
|
||||
}
|
||||
|
||||
// Gets the first argument of the instruction. InvalidIndex if there is no
|
||||
// such argument.
|
||||
@@ -240,12 +247,15 @@ class Inst : public Printable<Inst> {
|
||||
|
||||
private:
|
||||
friend class InstTestHelper;
|
||||
friend struct llvm::DenseMapInfo<Carbon::SemIR::Inst>;
|
||||
|
||||
// Table mapping instruction kinds to their argument kinds.
|
||||
static const std::pair<IdKind, IdKind> ArgKindTable[];
|
||||
|
||||
// Raw constructor, used for testing.
|
||||
// Raw constructor, used for testing and by DenseMapInfo.
|
||||
explicit Inst(InstKind kind, TypeId type_id, int32_t arg0, int32_t arg1)
|
||||
: Inst(kind.AsInt(), type_id, arg0, arg1) {}
|
||||
explicit Inst(int32_t kind, TypeId type_id, int32_t arg0, int32_t arg1)
|
||||
: kind_(kind), type_id_(type_id), arg0_(arg0), arg1_(arg1) {}
|
||||
|
||||
// Convert a field to its raw representation, used as `arg0_` / `arg1_`.
|
||||
@@ -264,7 +274,7 @@ class Inst : public Printable<Inst> {
|
||||
return BuiltinKind::FromInt(raw);
|
||||
}
|
||||
|
||||
InstKind kind_;
|
||||
int32_t kind_;
|
||||
TypeId type_id_;
|
||||
|
||||
// Use `As` to access arg0 and arg1.
|
||||
@@ -445,6 +455,30 @@ class InstBlockStore : public BlockValueStore<InstBlockId> {
|
||||
}
|
||||
};
|
||||
|
||||
// See common/hashing.h.
|
||||
inline auto CarbonHashValue(const Inst& value, uint64_t seed) -> HashCode {
|
||||
Hasher hasher(seed);
|
||||
hasher.Hash(value);
|
||||
return static_cast<HashCode>(hasher);
|
||||
}
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
template <>
|
||||
struct llvm::DenseMapInfo<Carbon::SemIR::Inst> {
|
||||
using Inst = Carbon::SemIR::Inst;
|
||||
static auto getEmptyKey() -> Inst {
|
||||
return Inst(-1, Carbon::SemIR::TypeId::Invalid, 0, 0);
|
||||
}
|
||||
static auto getTombstoneKey() -> Inst {
|
||||
return Inst(-2, Carbon::SemIR::TypeId::Invalid, 0, 0);
|
||||
}
|
||||
static auto getHashValue(const Inst& val) -> unsigned {
|
||||
return static_cast<uint64_t>(Carbon::HashValue(val));
|
||||
}
|
||||
static auto isEqual(const Inst& lhs, const Inst& rhs) -> bool {
|
||||
return std::memcmp(&lhs, &rhs, sizeof(Inst)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_SEM_IR_INST_H_
|
||||
|
||||
@@ -27,7 +27,7 @@ InstNamer::InstNamer(const Lex::TokenizedBuffer& tokenized_buffer,
|
||||
scopes.resize(static_cast<size_t>(GetScopeFor(NumberOfScopesTag())));
|
||||
|
||||
// Build the constants scope.
|
||||
CollectNamesInBlock(ScopeId::Constants, sem_ir.constants().GetAsVector());
|
||||
CollectNamesInBlock(ScopeId::Constants, sem_ir.constants().array_ref());
|
||||
|
||||
// Build the file scope.
|
||||
CollectNamesInBlock(ScopeId::File, sem_ir.top_inst_block_id());
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/sem_ir/inst_profile.h"
|
||||
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/inst.h"
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
// A function to profile an argument of an instruction.
|
||||
using ProfileArgFunction = auto(llvm::FoldingSetNodeID&, const File& sem_ir,
|
||||
int32_t arg) -> void;
|
||||
|
||||
// Profiling for unused arguments.
|
||||
static auto NullProfileArgFunction(llvm::FoldingSetNodeID& /*id*/,
|
||||
const File& /*sem_ir*/, int32_t arg)
|
||||
-> void {
|
||||
CARBON_CHECK(arg == IdBase::InvalidIndex)
|
||||
<< "Unexpected value for unused argument.";
|
||||
}
|
||||
|
||||
// Profiling for ID arguments that should participate in the instruction's
|
||||
// value.
|
||||
static auto DefaultProfileArgFunction(llvm::FoldingSetNodeID& id,
|
||||
const File& /*sem_ir*/, int32_t arg)
|
||||
-> void {
|
||||
id.AddInteger(arg);
|
||||
}
|
||||
|
||||
// Profiling for block ID arguments for which the content of the block should be
|
||||
// included.
|
||||
static auto InstBlockProfileArgFunction(llvm::FoldingSetNodeID& id,
|
||||
const File& sem_ir, int32_t arg)
|
||||
-> void {
|
||||
auto inst_block_id = InstBlockId(arg);
|
||||
if (!inst_block_id.is_valid()) {
|
||||
id.AddInteger(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
auto inst_block = sem_ir.inst_blocks().Get(inst_block_id);
|
||||
id.AddInteger(inst_block.size());
|
||||
for (auto inst_id : inst_block) {
|
||||
id.AddInteger(inst_id.index);
|
||||
}
|
||||
}
|
||||
|
||||
// Profiling for type block ID arguments for which the content of the block
|
||||
// should be included.
|
||||
static auto TypeBlockProfileArgFunction(llvm::FoldingSetNodeID& id,
|
||||
const File& sem_ir, int32_t arg)
|
||||
-> void {
|
||||
auto type_block_id = TypeBlockId(arg);
|
||||
if (!type_block_id.is_valid()) {
|
||||
id.AddInteger(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
auto type_block = sem_ir.type_blocks().Get(type_block_id);
|
||||
id.AddInteger(type_block.size());
|
||||
for (auto type_id : type_block) {
|
||||
id.AddInteger(type_id.index);
|
||||
}
|
||||
}
|
||||
|
||||
// Profiling for integer IDs.
|
||||
static auto IntProfileArgFunction(llvm::FoldingSetNodeID& id,
|
||||
const File& sem_ir, int32_t arg) -> void {
|
||||
sem_ir.ints().Get(IntId(arg)).Profile(id);
|
||||
}
|
||||
|
||||
// Profiling for real number IDs.
|
||||
static auto RealProfileArgFunction(llvm::FoldingSetNodeID& id,
|
||||
const File& sem_ir, int32_t arg) -> void {
|
||||
const auto& real = sem_ir.reals().Get(RealId(arg));
|
||||
// TODO: Profile the value rather than the syntactic form.
|
||||
real.mantissa.Profile(id);
|
||||
real.exponent.Profile(id);
|
||||
id.AddBoolean(real.is_decimal);
|
||||
}
|
||||
|
||||
// Profiling for BindNameInfo.
|
||||
static auto BindNameIdProfileArgFunction(llvm::FoldingSetNodeID& id,
|
||||
const File& sem_ir, int32_t arg)
|
||||
-> void {
|
||||
const auto& [name_id, enclosing_scope_id, bind_index] =
|
||||
sem_ir.bind_names().Get(BindNameId(arg));
|
||||
id.AddInteger(name_id.index);
|
||||
id.AddInteger(enclosing_scope_id.index);
|
||||
id.AddInteger(bind_index.index);
|
||||
}
|
||||
|
||||
// Profiles the given instruction argument, which is of the specified kind.
|
||||
static auto ProfileArg(llvm::FoldingSetNodeID& id, const File& sem_ir,
|
||||
IdKind arg_kind, int32_t arg) -> void {
|
||||
static constexpr std::array<ProfileArgFunction*, IdKind::NumValues>
|
||||
ProfileFunctions = [] {
|
||||
std::array<ProfileArgFunction*, IdKind::NumValues> array;
|
||||
array.fill(DefaultProfileArgFunction);
|
||||
array[IdKind::None.ToIndex()] = NullProfileArgFunction;
|
||||
array[IdKind::For<InstBlockId>.ToIndex()] = InstBlockProfileArgFunction;
|
||||
array[IdKind::For<TypeBlockId>.ToIndex()] = TypeBlockProfileArgFunction;
|
||||
array[IdKind::For<IntId>.ToIndex()] = IntProfileArgFunction;
|
||||
array[IdKind::For<RealId>.ToIndex()] = RealProfileArgFunction;
|
||||
array[IdKind::For<BindNameId>.ToIndex()] = BindNameIdProfileArgFunction;
|
||||
return array;
|
||||
}();
|
||||
ProfileFunctions[arg_kind.ToIndex()](id, sem_ir, arg);
|
||||
}
|
||||
|
||||
auto ProfileConstant(llvm::FoldingSetNodeID& id, const File& sem_ir, Inst inst)
|
||||
-> void {
|
||||
inst.kind().Profile(id);
|
||||
id.AddInteger(inst.type_id().index);
|
||||
auto arg_kinds = inst.ArgKinds();
|
||||
ProfileArg(id, sem_ir, arg_kinds.first, inst.arg0());
|
||||
ProfileArg(id, sem_ir, arg_kinds.second, inst.arg1());
|
||||
}
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
@@ -1,21 +0,0 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_SEM_IR_INST_PROFILE_H_
|
||||
#define CARBON_TOOLCHAIN_SEM_IR_INST_PROFILE_H_
|
||||
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/inst.h"
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
// Computes a profile of the given constant instruction, for canonicalization /
|
||||
// deduplication.
|
||||
auto ProfileConstant(llvm::FoldingSetNodeID& id, const File& sem_ir, Inst inst)
|
||||
-> void;
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_SEM_IR_INST_PROFILE_H_
|
||||
@@ -5,8 +5,6 @@
|
||||
#include "toolchain/sem_ir/name.h"
|
||||
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/inst_profile.h"
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user