Reduce the compile time of the typed_insts_test by a further 40% or so. (#3390)

Move the non-dependent portions of templates out into separate functions
so that we don't need to repeatedly generate code for them.
This commit is contained in:
Richard Smith
2023-11-11 00:31:55 +00:00
committed by GitHub
parent 109e7889dd
commit abedf08816
+47 -44
View File
@@ -31,14 +31,17 @@ namespace {
static_assert(Name::Kind == InstKind::Name);
#include "toolchain/sem_ir/inst_kind.def"
template <typename TypedInst>
auto CommonFieldOrder() -> void {
Inst inst = InstTestHelper::MakeInst(TypedInst::Kind, Parse::Node(1),
TypeId(2), 3, 4);
EXPECT_EQ(inst.kind(), TypedInst::Kind);
auto MakeInstWithNumberedFields(InstKind kind) -> Inst {
Inst inst = InstTestHelper::MakeInst(kind, Parse::Node(1), TypeId(2), 3, 4);
EXPECT_EQ(inst.kind(), kind);
EXPECT_EQ(inst.parse_node(), Parse::Node(1));
EXPECT_EQ(inst.type_id(), TypeId(2));
return inst;
}
template <typename TypedInst>
auto CommonFieldOrder() -> void {
Inst inst = MakeInstWithNumberedFields(TypedInst::Kind);
TypedInst typed = inst.As<TypedInst>();
if constexpr (HasParseNode<TypedInst>) {
EXPECT_EQ(typed.parse_node, Parse::Node(1));
@@ -57,24 +60,24 @@ TEST(TypedInstTest, CommonFieldOrder) {
#include "toolchain/sem_ir/inst_kind.def"
}
auto ExpectEqInsts(const Inst& inst1, const Inst& inst2,
bool compare_parse_node, bool compare_type_id) -> void {
EXPECT_EQ(inst1.kind(), inst2.kind());
if (compare_parse_node) {
EXPECT_EQ(inst1.parse_node(), inst2.parse_node());
}
if (compare_type_id) {
EXPECT_EQ(inst1.type_id(), inst2.type_id());
}
}
template <typename TypedInst>
auto RoundTrip() -> void {
Inst inst1 = InstTestHelper::MakeInst(TypedInst::Kind, Parse::Node(1),
TypeId(2), 3, 4);
EXPECT_EQ(inst1.kind(), TypedInst::Kind);
EXPECT_EQ(inst1.parse_node(), Parse::Node(1));
EXPECT_EQ(inst1.type_id(), TypeId(2));
Inst inst1 = MakeInstWithNumberedFields(TypedInst::Kind);
TypedInst typed1 = inst1.As<TypedInst>();
Inst inst2 = typed1;
EXPECT_EQ(inst1.kind(), inst2.kind());
if constexpr (HasParseNode<TypedInst>) {
EXPECT_EQ(inst1.parse_node(), inst2.parse_node());
}
if constexpr (HasTypeId<TypedInst>) {
EXPECT_EQ(inst1.type_id(), inst2.type_id());
}
ExpectEqInsts(inst1, inst2, HasParseNode<TypedInst>, HasTypeId<TypedInst>);
// If the typed instruction has no padding, we should get exactly the same
// thing if we convert back from an instruction.
@@ -87,9 +90,7 @@ auto RoundTrip() -> void {
// because the fields not carried by the typed instruction are lost. But they
// should be stable if we round-trip again.
Inst inst3 = typed2;
if constexpr (std::has_unique_object_representations_v<Inst>) {
EXPECT_EQ(std::memcmp(&inst2, &inst3, sizeof(Inst)), 0);
}
ExpectEqInsts(inst2, inst3, true, true);
}
TEST(TypedInstTest, RoundTrip) {
@@ -101,31 +102,35 @@ TEST(TypedInstTest, RoundTrip) {
#include "toolchain/sem_ir/inst_kind.def"
}
template <typename TypedInst>
auto StructLayout() -> void {
TypedInst typed =
InstTestHelper::MakeInst(TypedInst::Kind, Parse::Node(1), TypeId(2), 3, 4)
.template As<TypedInst>();
auto StructLayoutHelper(void* typed_inst, std::size_t typed_inst_size,
bool has_parse_node, bool has_type_id) -> void {
// Check that the memory representation of the typed instruction is what we
// expect.
// TODO: Struct layout is not guaranteed, and this test could fail in some
// build environment. If so, we should disable it.
int32_t fields[4] = {};
int field = 0;
if constexpr (HasParseNode<TypedInst>) {
if (has_parse_node) {
fields[field++] = 1;
}
if constexpr (HasTypeId<TypedInst>) {
if (has_type_id) {
fields[field++] = 2;
}
fields[field++] = 3;
fields[field++] = 4;
ASSERT_LE(sizeof(TypedInst), sizeof(fields));
ASSERT_LE(typed_inst_size, sizeof(int32_t) * field);
EXPECT_EQ(std::memcmp(&fields, typed_inst, typed_inst_size), 0);
}
template <typename TypedInst>
auto StructLayout() -> void {
// We can only do this check if the typed instruction has no padding.
if constexpr (std::has_unique_object_representations_v<TypedInst>) {
EXPECT_EQ(std::memcmp(&fields, &typed, sizeof(TypedInst)), 0);
TypedInst typed =
MakeInstWithNumberedFields(TypedInst::Kind).template As<TypedInst>();
StructLayoutHelper(&typed, sizeof(typed), HasParseNode<TypedInst>,
HasTypeId<TypedInst>);
}
}
@@ -138,22 +143,20 @@ TEST(TypedInstTest, StructLayout) {
#include "toolchain/sem_ir/inst_kind.def"
}
template <typename TypedInst>
auto InstKindMatches() {
// TypedInst::Kind is an InstKind::Definition that extends InstKind, but
// has different definitions of the `ir_name()` and `terminator_kind()`
// methods. Here we test that values returned by the two different versions
// of those functions match.
InstKind as_kind = TypedInst::Kind;
EXPECT_EQ(TypedInst::Kind.ir_name(), as_kind.ir_name());
EXPECT_EQ(TypedInst::Kind.terminator_kind(), as_kind.terminator_kind());
auto InstKindMatches(const InstKind::Definition& def, InstKind kind) {
EXPECT_EQ(def.ir_name(), kind.ir_name());
EXPECT_EQ(def.terminator_kind(), kind.terminator_kind());
}
TEST(TypedInstTest, InstKindMatches) {
#define CARBON_SEM_IR_INST_KIND(Name) \
{ \
SCOPED_TRACE(#Name); \
InstKindMatches<Name>(); \
// TypedInst::Kind is an InstKind::Definition that extends InstKind, but has
// different definitions of the `ir_name()` and `terminator_kind()` methods.
// Here we test that values returned by the two different versions of those
// functions match.
#define CARBON_SEM_IR_INST_KIND(Name) \
{ \
SCOPED_TRACE(#Name); \
InstKindMatches(Name::Kind, Name::Kind); \
}
#include "toolchain/sem_ir/inst_kind.def"
}