From 5c8fa6ad5c5fe4d22ff9a46bd2d66f4f01135198 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 22 May 2024 17:48:49 -0700 Subject: [PATCH] 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. --- common/hashing.h | 10 ++ common/hashing_test.cpp | 1 + toolchain/base/BUILD | 1 + toolchain/base/value_store.h | 118 ++++++++++++++--- toolchain/check/context.cpp | 6 +- toolchain/check/context.h | 8 +- toolchain/check/eval.cpp | 7 +- toolchain/check/subst.cpp | 4 +- .../multifile_raw_and_textual_ir.carbon | 2 - .../basics/no_prelude/multifile_raw_ir.carbon | 2 - .../no_prelude/raw_and_textual_ir.carbon | 25 +--- .../testdata/basics/no_prelude/raw_ir.carbon | 25 +--- .../check/testdata/builtins/float/div.carbon | 14 +- .../check/testdata/builtins/float/eq.carbon | 26 ++-- .../testdata/builtins/float/greater.carbon | 54 ++++---- .../testdata/builtins/float/greater_eq.carbon | 60 ++++----- .../check/testdata/builtins/float/less.carbon | 60 ++++----- .../testdata/builtins/float/less_eq.carbon | 54 ++++---- .../check/testdata/builtins/float/neq.carbon | 14 +- .../driver/testdata/dump_shared_values.carbon | 7 +- toolchain/sem_ir/BUILD | 6 +- toolchain/sem_ir/bind_name.h | 76 +++++++++++ toolchain/sem_ir/block_value_store.h | 57 ++++++++ toolchain/sem_ir/constant.cpp | 76 ++--------- toolchain/sem_ir/constant.h | 23 +--- toolchain/sem_ir/copy_on_write_block.h | 6 + toolchain/sem_ir/file.h | 33 ++--- toolchain/sem_ir/formatter.cpp | 2 +- toolchain/sem_ir/ids.h | 7 + toolchain/sem_ir/inst.cpp | 4 +- toolchain/sem_ir/inst.h | 48 ++++++- toolchain/sem_ir/inst_namer.cpp | 2 +- toolchain/sem_ir/inst_profile.cpp | 123 ------------------ toolchain/sem_ir/inst_profile.h | 21 --- toolchain/sem_ir/name.cpp | 2 - 35 files changed, 479 insertions(+), 505 deletions(-) create mode 100644 toolchain/sem_ir/bind_name.h delete mode 100644 toolchain/sem_ir/inst_profile.cpp delete mode 100644 toolchain/sem_ir/inst_profile.h diff --git a/common/hashing.h b/common/hashing.h index 9a37b91a5d53..2e8edfdc2671 100644 --- a/common/hashing.h +++ b/common/hashing.h @@ -279,6 +279,16 @@ class Hasher { // state along with the contents. auto HashSizedBytes(llvm::ArrayRef bytes) -> void; + // Incorporate a dynamically sized sequence of bytes represented as an array + // of objects into the hasher's state. + template + requires std::has_unique_object_representations_v + auto HashSizedBytes(llvm::ArrayRef data) -> void { + HashSizedBytes(llvm::ArrayRef( + reinterpret_cast(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. diff --git a/common/hashing_test.cpp b/common/hashing_test.cpp index 473b163be481..23553901c11d 100644 --- a/common/hashing_test.cpp +++ b/common/hashing_test.cpp @@ -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); diff --git a/toolchain/base/BUILD b/toolchain/base/BUILD index a3ae9bb6249a..d5a6f84a20ba 100644 --- a/toolchain/base/BUILD +++ b/toolchain/base/BUILD @@ -38,6 +38,7 @@ cc_library( ":index_base", ":yaml", "//common:check", + "//common:hashing", "//common:ostream", "@llvm-project//llvm:Support", ], diff --git a/toolchain/base/value_store.h b/toolchain/base/value_store.h index e925f0e51ba8..c20a75b1e112 100644 --- a/toolchain/base/value_store.h +++ b/toolchain/base/value_store.h @@ -8,6 +8,7 @@ #include #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 { public: auto Print(llvm::raw_ostream& output_stream) const -> void { @@ -48,9 +49,12 @@ class Real : public Printable { 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 { - 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 { }; 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 { - 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 { }; 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 { - 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 { - 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, 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 > +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 { + 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, 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 values_; + llvm::DenseMap map_; +}; + // Storage for StringRefs. The caller is responsible for ensuring storage is // allocated. template <> -class ValueStore : public Yaml::Printable> { +class CanonicalValueStore + : public Yaml::Printable> { 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 : public Yaml::Printable> { template class StringStoreWrapper : public Printable> { public: - explicit StringStoreWrapper(ValueStore* values) : values_(values) {} + explicit StringStoreWrapper(CanonicalValueStore* values) + : values_(values) {} auto Add(llvm::StringRef value) -> IdT { return IdT(values_->Add(value).index); @@ -263,9 +341,11 @@ class StringStoreWrapper : public Printable> { auto size() const -> size_t { return values_->size(); } private: - ValueStore* values_; + CanonicalValueStore* values_; }; +using FloatValueStore = CanonicalValueStore; + // 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 { @@ -283,12 +363,12 @@ class SharedValueStores : public Yaml::Printable { auto identifiers() const -> const StringStoreWrapper& { return identifiers_; } - auto ints() -> ValueStore& { return ints_; } - auto ints() const -> const ValueStore& { return ints_; } + auto ints() -> CanonicalValueStore& { return ints_; } + auto ints() const -> const CanonicalValueStore& { return ints_; } auto reals() -> ValueStore& { return reals_; } auto reals() const -> const ValueStore& { return reals_; } - auto floats() -> ValueStore& { return floats_; } - auto floats() const -> const ValueStore& { return floats_; } + auto floats() -> FloatValueStore& { return floats_; } + auto floats() const -> const FloatValueStore& { return floats_; } auto string_literal_values() -> StringStoreWrapper& { return string_literal_values_; } @@ -313,11 +393,11 @@ class SharedValueStores : public Yaml::Printable { } private: - ValueStore ints_; + CanonicalValueStore ints_; ValueStore reals_; - ValueStore floats_; + FloatValueStore floats_; - ValueStore strings_; + CanonicalValueStore strings_; StringStoreWrapper identifiers_; StringStoreWrapper string_literal_values_; }; diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 1a595aab0f79..58fa95fc97f8 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -1076,10 +1076,8 @@ auto Context::GetStructType(SemIR::InstBlockId refs_id) -> SemIR::TypeId { auto Context::GetTupleType(llvm::ArrayRef 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(*this, type_blocks().Add(type_ids)); + return GetTypeImpl(*this, + type_blocks().AddCanonical(type_ids)); } auto Context::GetAssociatedEntityType(SemIR::InterfaceId interface_id, diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 022b711bebd4..ef36969ed5fd 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -341,15 +341,13 @@ class Context { auto identifiers() -> StringStoreWrapper& { return sem_ir().identifiers(); } - auto ints() -> ValueStore& { return sem_ir().ints(); } + auto ints() -> CanonicalValueStore& { return sem_ir().ints(); } auto reals() -> ValueStore& { return sem_ir().reals(); } - auto floats() -> ValueStore& { return sem_ir().floats(); } + auto floats() -> FloatValueStore& { return sem_ir().floats(); } auto string_literal_values() -> StringStoreWrapper& { return sem_ir().string_literal_values(); } - auto bind_names() -> ValueStore& { - return sem_ir().bind_names(); - } + auto bind_names() -> SemIR::BindNameStore& { return sem_ir().bind_names(); } auto functions() -> ValueStore& { return sem_ir().functions(); } diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 79da49a1986e..5c795228e0ef 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -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); } diff --git a/toolchain/check/subst.cpp b/toolchain/check/subst.cpp index 04e270ae0daa..a5dd133d8a06 100644 --- a/toolchain/check/subst.cpp +++ b/toolchain/check/subst.cpp @@ -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: { auto old_type_block_id = static_cast(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; diff --git a/toolchain/check/testdata/basics/no_prelude/multifile_raw_and_textual_ir.carbon b/toolchain/check/testdata/basics/no_prelude/multifile_raw_and_textual_ir.carbon index 40d16ba0d1f0..8692c02d4d5f 100644 --- a/toolchain/check/testdata/basics/no_prelude/multifile_raw_and_textual_ir.carbon +++ b/toolchain/check/testdata/basics/no_prelude/multifile_raw_and_textual_ir.carbon @@ -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, 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, type: type0} // CHECK:STDOUT: 'inst+1': {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1} diff --git a/toolchain/check/testdata/basics/no_prelude/multifile_raw_ir.carbon b/toolchain/check/testdata/basics/no_prelude/multifile_raw_ir.carbon index 1537f15e8e2c..f11781721dad 100644 --- a/toolchain/check/testdata/basics/no_prelude/multifile_raw_ir.carbon +++ b/toolchain/check/testdata/basics/no_prelude/multifile_raw_ir.carbon @@ -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, 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, type: type0} // CHECK:STDOUT: 'inst+1': {kind: FunctionDecl, arg0: function0, arg1: empty, type: type1} diff --git a/toolchain/check/testdata/basics/no_prelude/raw_and_textual_ir.carbon b/toolchain/check/testdata/basics/no_prelude/raw_and_textual_ir.carbon index dde218df52d2..4d81c4653d03 100644 --- a/toolchain/check/testdata/basics/no_prelude/raw_and_textual_ir.carbon +++ b/toolchain/check/testdata/basics/no_prelude/raw_and_textual_ir.carbon @@ -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: ... diff --git a/toolchain/check/testdata/basics/no_prelude/raw_ir.carbon b/toolchain/check/testdata/basics/no_prelude/raw_ir.carbon index 4ba5892a5d62..ef978d5dfc7d 100644 --- a/toolchain/check/testdata/basics/no_prelude/raw_ir.carbon +++ b/toolchain/check/testdata/basics/no_prelude/raw_ir.carbon @@ -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: ... diff --git a/toolchain/check/testdata/builtins/float/div.carbon b/toolchain/check/testdata/builtins/float/div.carbon index d29635bb54e3..b6efaa45bbaf 100644 --- a/toolchain/check/testdata/builtins/float/div.carbon +++ b/toolchain/check/testdata/builtins/float/div.carbon @@ -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: diff --git a/toolchain/check/testdata/builtins/float/eq.carbon b/toolchain/check/testdata/builtins/float/eq.carbon index bd1314f3a1d3..d189a04017dd 100644 --- a/toolchain/check/testdata/builtins/float/eq.carbon +++ b/toolchain/check/testdata/builtins/float/eq.carbon @@ -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: diff --git a/toolchain/check/testdata/builtins/float/greater.carbon b/toolchain/check/testdata/builtins/float/greater.carbon index 4b55812ba79f..3c61fd32141c 100644 --- a/toolchain/check/testdata/builtins/float/greater.carbon +++ b/toolchain/check/testdata/builtins/float/greater.carbon @@ -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: diff --git a/toolchain/check/testdata/builtins/float/greater_eq.carbon b/toolchain/check/testdata/builtins/float/greater_eq.carbon index aef2f347af8e..5c02ab4b6b66 100644 --- a/toolchain/check/testdata/builtins/float/greater_eq.carbon +++ b/toolchain/check/testdata/builtins/float/greater_eq.carbon @@ -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: diff --git a/toolchain/check/testdata/builtins/float/less.carbon b/toolchain/check/testdata/builtins/float/less.carbon index e33309b0beed..336c1e643984 100644 --- a/toolchain/check/testdata/builtins/float/less.carbon +++ b/toolchain/check/testdata/builtins/float/less.carbon @@ -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: diff --git a/toolchain/check/testdata/builtins/float/less_eq.carbon b/toolchain/check/testdata/builtins/float/less_eq.carbon index 5f0d9ac1d9d9..ce8eb1886ace 100644 --- a/toolchain/check/testdata/builtins/float/less_eq.carbon +++ b/toolchain/check/testdata/builtins/float/less_eq.carbon @@ -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: diff --git a/toolchain/check/testdata/builtins/float/neq.carbon b/toolchain/check/testdata/builtins/float/neq.carbon index 69852b523089..148beed225a9 100644 --- a/toolchain/check/testdata/builtins/float/neq.carbon +++ b/toolchain/check/testdata/builtins/float/neq.carbon @@ -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: diff --git a/toolchain/driver/testdata/dump_shared_values.carbon b/toolchain/driver/testdata/dump_shared_values.carbon index 18e61ba99685..b2d4b95cd30f 100644 --- a/toolchain/driver/testdata/dump_shared_values.carbon +++ b/toolchain/driver/testdata/dump_shared_values.carbon @@ -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 diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 288a5c4e1ff5..27194fb06fea 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -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", diff --git a/toolchain/sem_ir/bind_name.h b/toolchain/sem_ir/bind_name.h new file mode 100644 index 000000000000..6713f7177d6e --- /dev/null +++ b/toolchain/sem_ir/bind_name.h @@ -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 { + 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(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(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 { + 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 + canonical_ids_; +}; + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_BIND_NAME_H_ diff --git a/toolchain/sem_ir/block_value_store.h b/toolchain/sem_ir/block_value_store.h index ddb028a39ef7..16819ec931d7 100644 --- a/toolchain/sem_ir/block_value_store.h +++ b/toolchain/sem_ir/block_value_store.h @@ -7,6 +7,7 @@ #include +#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> { 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 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> { } 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 data; + + // See common/hashing.h. + friend auto CarbonHashValue(CanonicalBlock block, uint64_t seed) + -> HashCode { + Hasher hasher(seed); + hasher.HashSizedBytes(block.data); + return static_cast(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(0))}; + } + static auto getTombstoneKey() -> CanonicalBlock { + return CanonicalBlock{llvm::ArrayRef(SpecialData, 1)}; + } + static auto getHashValue(CanonicalBlock val) -> unsigned { + return static_cast(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 { @@ -103,6 +158,8 @@ class BlockValueStore : public Yaml::Printable> { llvm::BumpPtrAllocator* allocator_; ValueStore values_; + llvm::DenseMap + canonical_blocks_; }; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/constant.cpp b/toolchain/sem_ir/constant.cpp index 5680ff79792f..7862e2015d5a 100644 --- a/toolchain/sem_ir/constant.cpp +++ b/toolchain/sem_ir/constant.cpp @@ -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 { - llvm::SmallVector 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 diff --git a/toolchain/sem_ir/constant.h b/toolchain/sem_ir/constant.h index de5348af9e37..289ad180eadf 100644 --- a/toolchain/sem_ir/constant.h +++ b/toolchain/sem_ir/constant.h @@ -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; + auto array_ref() const -> llvm::ArrayRef { 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 constants_; + File& sem_ir_; + llvm::DenseMap map_; + llvm::SmallVector constants_; }; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/copy_on_write_block.h b/toolchain/sem_ir/copy_on_write_block.h index b89ef8ac408f..82364abf9318 100644 --- a/toolchain/sem_ir/copy_on_write_block.h +++ b/toolchain/sem_ir/copy_on_write_block.h @@ -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 { diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index cff0282e91f6..2a57691f7ee5 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -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 { - 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 { public: @@ -108,16 +93,16 @@ class File : public Printable { auto identifiers() const -> const StringStoreWrapper& { return value_stores_->identifiers(); } - auto ints() -> ValueStore& { return value_stores_->ints(); } - auto ints() const -> const ValueStore& { + auto ints() -> CanonicalValueStore& { return value_stores_->ints(); } + auto ints() const -> const CanonicalValueStore& { return value_stores_->ints(); } auto reals() -> ValueStore& { return value_stores_->reals(); } auto reals() const -> const ValueStore& { return value_stores_->reals(); } - auto floats() -> ValueStore& { return value_stores_->floats(); } - auto floats() const -> const ValueStore& { + auto floats() -> FloatValueStore& { return value_stores_->floats(); } + auto floats() const -> const FloatValueStore& { return value_stores_->floats(); } auto string_literal_values() -> StringStoreWrapper& { @@ -128,10 +113,8 @@ class File : public Printable { return value_stores_->string_literal_values(); } - auto bind_names() -> ValueStore& { return bind_names_; } - auto bind_names() const -> const ValueStore& { - return bind_names_; - } + auto bind_names() -> BindNameStore& { return bind_names_; } + auto bind_names() const -> const BindNameStore& { return bind_names_; } auto functions() -> ValueStore& { return functions_; } auto functions() const -> const ValueStore& { return functions_; } auto classes() -> ValueStore& { return classes_; } @@ -208,7 +191,7 @@ class File : public Printable { std::string filename_; // Storage for bind names. - ValueStore bind_names_; + BindNameStore bind_names_; // Storage for callable objects. ValueStore functions_; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index f5edafa7e5d1..39be4f57fd82 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -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"; } diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 8a087b9cb4dd..c4e23280d871 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -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. // The ID of a function. struct FunctionId : public IdBase, public Printable { @@ -538,6 +540,9 @@ struct TypeBlockId : public IdBase, public Printable { using ElementType = TypeId; using ValueType = llvm::MutableArrayRef; + // 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 { } }; +constexpr TypeBlockId TypeBlockId::Invalid = TypeBlockId(InvalidIndex); + // An index for element access, for structs, tuples, and classes. struct ElementIndex : public IndexBase, public Printable { using IndexBase::IndexBase; diff --git a/toolchain/sem_ir/inst.cpp b/toolchain/sem_ir/inst.cpp index ec73912a627a..deca639d8933 100644 --- a/toolchain/sem_ir/inst.cpp +++ b/toolchain/sem_ir/inst.cpp @@ -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()); \ diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index 7cdcf78391b0..a19f31d8d264 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -9,6 +9,7 @@ #include #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 { // 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) { - 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) { type_id_ = typed_inst.type_id; } @@ -205,7 +208,9 @@ class Inst : public Printable { } } - auto kind() const -> InstKind { return kind_; } + auto kind() const -> InstKind { + return InstKind::Make(static_cast(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 { } // Gets the kinds of IDs used for arg0 and arg1 of this instruction. - auto ArgKinds() const -> std::pair { return ArgKinds(kind_); } + auto ArgKinds() const -> std::pair { + 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 { private: friend class InstTestHelper; + friend struct llvm::DenseMapInfo; // Table mapping instruction kinds to their argument kinds. static const std::pair 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 { 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 { } }; +// See common/hashing.h. +inline auto CarbonHashValue(const Inst& value, uint64_t seed) -> HashCode { + Hasher hasher(seed); + hasher.Hash(value); + return static_cast(hasher); +} + } // namespace Carbon::SemIR +template <> +struct llvm::DenseMapInfo { + 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(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_ diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index a1d84ac45a7b..c1d5fee30e3e 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -27,7 +27,7 @@ InstNamer::InstNamer(const Lex::TokenizedBuffer& tokenized_buffer, scopes.resize(static_cast(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()); diff --git a/toolchain/sem_ir/inst_profile.cpp b/toolchain/sem_ir/inst_profile.cpp deleted file mode 100644 index 86006c2b51ec..000000000000 --- a/toolchain/sem_ir/inst_profile.cpp +++ /dev/null @@ -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 - ProfileFunctions = [] { - std::array array; - array.fill(DefaultProfileArgFunction); - array[IdKind::None.ToIndex()] = NullProfileArgFunction; - array[IdKind::For.ToIndex()] = InstBlockProfileArgFunction; - array[IdKind::For.ToIndex()] = TypeBlockProfileArgFunction; - array[IdKind::For.ToIndex()] = IntProfileArgFunction; - array[IdKind::For.ToIndex()] = RealProfileArgFunction; - array[IdKind::For.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 diff --git a/toolchain/sem_ir/inst_profile.h b/toolchain/sem_ir/inst_profile.h deleted file mode 100644 index 382a379e87b9..000000000000 --- a/toolchain/sem_ir/inst_profile.h +++ /dev/null @@ -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_ diff --git a/toolchain/sem_ir/name.cpp b/toolchain/sem_ir/name.cpp index 27a28c9449cf..05b0b9af1eaa 100644 --- a/toolchain/sem_ir/name.cpp +++ b/toolchain/sem_ir/name.cpp @@ -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 {