Skip to content

Commit 81f5fd4

Browse files
committed
Refactor: optimize diagnostic table storage
diagnostic_message_arg_info takes two bytes: one for the offset and one for the type. The diagnostic table contains a lot of diagnostic_message_arg_info-s, so these bytes add up. Shrink diagnostic_message_arg_info by storing the offset in 5 bits and the type in 3 bits (totalling 8 bits aka one byte). This commit should not change behavior.
1 parent 434186f commit 81f5fd4

File tree

4 files changed

+50
-16
lines changed

4 files changed

+50
-16
lines changed

src/diagnostic.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,8 @@ get_diagnostic_message_arg_type<string8_view>() noexcept {
9898
template <class ArgType>
9999
constexpr diagnostic_message_arg_info make_diagnostic_message_arg_info(
100100
std::uint8_t offset) noexcept {
101-
return diagnostic_message_arg_info{
102-
.offset = offset,
103-
.type = get_diagnostic_message_arg_type<ArgType>(),
104-
};
101+
return diagnostic_message_arg_info(
102+
offset, get_diagnostic_message_arg_type<ArgType>());
105103
}
106104

107105
template <class Error>

src/quick-lint-js/diagnostic-formatter.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class diagnostic_formatter_base {
3636
return *reinterpret_cast<const source_code_span*>(arg_data);
3737

3838
case diagnostic_arg_type::char8:
39+
case diagnostic_arg_type::invalid:
3940
case diagnostic_arg_type::statement_kind:
4041
case diagnostic_arg_type::string8_view:
4142
case diagnostic_arg_type::variable_kind:
@@ -63,6 +64,7 @@ class diagnostic_formatter_base {
6364
case diagnostic_arg_type::string8_view:
6465
return *reinterpret_cast<const string8_view*>(arg_data);
6566

67+
case diagnostic_arg_type::invalid:
6668
case diagnostic_arg_type::statement_kind:
6769
case diagnostic_arg_type::variable_kind:
6870
QLJS_UNREACHABLE();
@@ -81,6 +83,7 @@ class diagnostic_formatter_base {
8183

8284
case diagnostic_arg_type::char8:
8385
case diagnostic_arg_type::identifier:
86+
case diagnostic_arg_type::invalid:
8487
case diagnostic_arg_type::source_code_span:
8588
case diagnostic_arg_type::string8_view:
8689
case diagnostic_arg_type::variable_kind:
@@ -100,6 +103,7 @@ class diagnostic_formatter_base {
100103

101104
case diagnostic_arg_type::char8:
102105
case diagnostic_arg_type::identifier:
106+
case diagnostic_arg_type::invalid:
103107
case diagnostic_arg_type::source_code_span:
104108
case diagnostic_arg_type::string8_view:
105109
case diagnostic_arg_type::variable_kind:
@@ -114,7 +118,7 @@ class diagnostic_formatter_base {
114118
int arg_index) noexcept {
115119
const diagnostic_message_arg_info& arg_info = message_info.args[arg_index];
116120
const void* arg_data =
117-
reinterpret_cast<const char*>(diagnostic) + arg_info.offset;
121+
reinterpret_cast<const char*>(diagnostic) + arg_info.offset();
118122
return std::make_pair(arg_data, arg_info.type);
119123
}
120124
};

src/quick-lint-js/diagnostic.h

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
#ifndef QUICK_LINT_JS_DIAGNOSTIC_H
55
#define QUICK_LINT_JS_DIAGNOSTIC_H
66

7+
#include <cstddef>
78
#include <cstdint>
89
#include <optional>
10+
#include <quick-lint-js/assert.h>
911
#include <quick-lint-js/language.h>
1012
#include <quick-lint-js/translation.h>
13+
#include <quick-lint-js/warning.h>
1114
#include <string_view>
1215

1316
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105191
@@ -27,6 +30,8 @@ enum class diagnostic_severity : std::uint8_t {
2730
};
2831

2932
enum class diagnostic_arg_type : std::uint8_t {
33+
invalid = 0,
34+
3035
char8,
3136
identifier,
3237
source_code_span,
@@ -36,8 +41,35 @@ enum class diagnostic_arg_type : std::uint8_t {
3641
};
3742

3843
struct diagnostic_message_arg_info {
39-
std::uint8_t offset QLJS_WORK_AROUND_GCC_BUG_105191;
40-
diagnostic_arg_type type QLJS_WORK_AROUND_GCC_BUG_105191;
44+
// offset_shift is how many bits are removed in compact_offset.
45+
//
46+
// For example, if offset_shift is 3, then an arg must be 8-byte aligned.
47+
static constexpr int offset_shift = 1;
48+
static constexpr int offset_mask = (1 << offset_shift) - 1;
49+
static constexpr int offset_bits = 5;
50+
51+
/*implicit*/ constexpr diagnostic_message_arg_info() noexcept
52+
: compact_offset(0), type(diagnostic_arg_type::invalid) {}
53+
54+
QLJS_WARNING_PUSH
55+
QLJS_WARNING_IGNORE_CLANG("-Wimplicit-int-conversion")
56+
QLJS_WARNING_IGNORE_GCC("-Wconversion")
57+
/*implicit*/ constexpr diagnostic_message_arg_info(
58+
std::size_t offset, diagnostic_arg_type type) noexcept
59+
: compact_offset(offset >> offset_shift), type(type) {
60+
// offset should be small.
61+
QLJS_CONSTEXPR_ASSERT((offset >> offset_shift) < (1 << offset_bits));
62+
// offset should be aligned.
63+
QLJS_CONSTEXPR_ASSERT((offset & offset_mask) == 0);
64+
}
65+
QLJS_WARNING_POP
66+
67+
constexpr std::size_t offset() const noexcept {
68+
return static_cast<std::size_t>(this->compact_offset << offset_shift);
69+
}
70+
71+
std::uint8_t compact_offset : offset_bits QLJS_WORK_AROUND_GCC_BUG_105191;
72+
diagnostic_arg_type type : (8 - offset_bits) QLJS_WORK_AROUND_GCC_BUG_105191;
4173
};
4274

4375
struct diagnostic_message_info {

test/test-diagnostic.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ TEST(test_diagnostic, diagnostic_info) {
2828
EXPECT_STREQ(source_code_messages.translate(info.messages[0].format),
2929
"if statement needs parentheses around condition");
3030
EXPECT_EQ(
31-
info.messages[0].args[0].offset,
31+
info.messages[0].args[0].offset(),
3232
offsetof(error_expected_parentheses_around_if_condition, condition));
3333
EXPECT_EQ(info.messages[0].args[0].type,
3434
diagnostic_arg_type::source_code_span);
@@ -42,11 +42,11 @@ TEST(test_diagnostic, diagnostic_info) {
4242
EXPECT_EQ(info.severity, diagnostic_severity::error);
4343
EXPECT_STREQ(source_code_messages.translate(info.messages[0].format),
4444
"if statement is missing '{1}' around condition");
45-
EXPECT_EQ(info.messages[0].args[0].offset,
45+
EXPECT_EQ(info.messages[0].args[0].offset(),
4646
offsetof(error_expected_parenthesis_around_if_condition, where));
4747
EXPECT_EQ(info.messages[0].args[0].type,
4848
diagnostic_arg_type::source_code_span);
49-
EXPECT_EQ(info.messages[0].args[1].offset,
49+
EXPECT_EQ(info.messages[0].args[1].offset(),
5050
offsetof(error_expected_parenthesis_around_if_condition, token));
5151
EXPECT_EQ(info.messages[0].args[1].type, diagnostic_arg_type::char8);
5252
EXPECT_FALSE(info.messages[1].format.valid());
@@ -60,12 +60,12 @@ TEST(test_diagnostic, diagnostic_info) {
6060
EXPECT_STREQ(source_code_messages.translate(info.messages[0].format),
6161
"function called before declaration in block scope: {0}");
6262
EXPECT_EQ(
63-
info.messages[0].args[0].offset,
63+
info.messages[0].args[0].offset(),
6464
offsetof(error_function_call_before_declaration_in_block_scope, use));
6565
EXPECT_EQ(info.messages[0].args[0].type, diagnostic_arg_type::identifier);
6666
EXPECT_STREQ(source_code_messages.translate(info.messages[1].format),
6767
"function declared here");
68-
EXPECT_EQ(info.messages[1].args[0].offset,
68+
EXPECT_EQ(info.messages[1].args[0].offset(),
6969
offsetof(error_function_call_before_declaration_in_block_scope,
7070
declaration));
7171
EXPECT_EQ(info.messages[1].args[0].type, diagnostic_arg_type::identifier);
@@ -79,25 +79,25 @@ TEST(test_diagnostic, diagnostic_info) {
7979
EXPECT_STREQ(source_code_messages.translate(info.messages[0].format),
8080
"missing body for {1:headlinese}");
8181
EXPECT_EQ(
82-
info.messages[0].args[0].offset,
82+
info.messages[0].args[0].offset(),
8383
offsetof(error_class_statement_not_allowed_in_body, expected_body));
8484
EXPECT_EQ(info.messages[0].args[0].type,
8585
diagnostic_arg_type::source_code_span);
8686
EXPECT_EQ(
87-
info.messages[0].args[1].offset,
87+
info.messages[0].args[1].offset(),
8888
offsetof(error_class_statement_not_allowed_in_body, kind_of_statement));
8989
EXPECT_EQ(info.messages[0].args[1].type,
9090
diagnostic_arg_type::statement_kind);
9191
EXPECT_STREQ(
9292
source_code_messages.translate(info.messages[1].format),
9393
"a class statement is not allowed as the body of {1:singular}");
9494
EXPECT_EQ(
95-
info.messages[1].args[0].offset,
95+
info.messages[1].args[0].offset(),
9696
offsetof(error_class_statement_not_allowed_in_body, class_keyword));
9797
EXPECT_EQ(info.messages[1].args[0].type,
9898
diagnostic_arg_type::source_code_span);
9999
EXPECT_EQ(
100-
info.messages[1].args[1].offset,
100+
info.messages[1].args[1].offset(),
101101
offsetof(error_class_statement_not_allowed_in_body, kind_of_statement));
102102
EXPECT_EQ(info.messages[1].args[1].type,
103103
diagnostic_arg_type::statement_kind);

0 commit comments

Comments
 (0)