mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-30 16:25:01 +01:00
81dfb2b29b115df27f22cd7e8564d40c360d4e22
42
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
08f24551ec |
Add bit packing to NodeImpl (#4651)
Just a small packing optimization. We currently have 222 `NodeKinds`, so this reduces us to just 30ish more we can add without needing to pack more. However, if we did, there would be a couple options for bringing the count down by reusing `NodeKinds` and disambiguating based on the token kind (the 29 infix operators as an example). Or we could just undo this. I'm expecting this to yield a small improvement. I'll see if I can get better numbers since my machine's not really reliable, but here are some basic values. Also suggesting to draw the use of `::RawEnumType` for `TokenKind`, since bit packing appears to work without it. Hoping the `static_assert` is easier for people to understand the size of the field. With the change: ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark Time CPU Iterations Bytes Lines Tokens ---------------------------------------------------------------------------------------------------------------------------- BM_CompileAPIFileDenseDecls<Phase::Parse>/256 50399 ns 50359 ns 14336 104.588M/s 3.87217M/s 21.8629M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/1024 237823 ns 237629 ns 3072 136.721M/s 4.11986M/s 24.2058M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/4096 997645 ns 996771 ns 768 142.343M/s 4.04105M/s 23.9363M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/16384 4020308 ns 4018319 ns 192 152.041M/s 4.05966M/s 24.0874M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/65536 16691390 ns 16683058 ns 48 151.317M/s 3.92374M/s 23.2936M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/262144 75265735 ns 75233476 ns 8 135.842M/s 3.48421M/s 20.6862M/s ``` Without the change: ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark Time CPU Iterations Bytes Lines Tokens ---------------------------------------------------------------------------------------------------------------------------- BM_CompileAPIFileDenseDecls<Phase::Parse>/256 51515 ns 51480 ns 13312 102.312M/s 3.78789M/s 21.387M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/1024 241040 ns 240900 ns 3072 134.865M/s 4.06392M/s 23.8771M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/4096 985593 ns 984657 ns 768 144.094M/s 4.09077M/s 24.2308M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/16384 4109327 ns 4105496 ns 192 148.813M/s 3.97345M/s 23.576M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/65536 17459655 ns 17446006 ns 48 144.7M/s 3.75215M/s 22.275M/s BM_CompileAPIFileDenseDecls<Phase::Parse>/262144 80802815 ns 80737489 ns 8 126.581M/s 3.24668M/s 19.276M/s ``` --------- Co-authored-by: Chandler Carruth <chandlerc@gmail.com> |
||
|
|
3ce0df67bb |
Add Dump functions to Check, Parse, and Lex (#4669)
- Provide `Check::Dump(context, arg)` and similar. - gdb and lldb should do contextual lookup, and `call Dump(*this, Lex::TokenIndex::Invalid)` has been tested with gdb. - Since this is only for debug, keeps the functions fully separated from code. - Uses alwayslink to ensure objects are correctly linked, even though there are no calls. - `-Wno-missing-prototypes` is needed when we don't have forward declarations. - Code is not linked in opt builds, using `#ifndef NDEBUG`. - This probably could be doing something in BUILD files with a `select()`, but the `#ifndef` seemed easier. This is based on #4620, but uses free functions instead of member functions. Co-authored-by: Dana Jansens <danakj@orodu.net> --------- Co-authored-by: danakj <danakj@orodu.net> |
||
|
|
bc24a6c5d8 |
Refactor IdBase to provide CRTP-based printing (#4626)
This removes a lot of boilerplate `Print` functions in favor of a CRTP-based approach that uses a `Label` field as an automatic prefix. This `Label` is also made available for other purposes, particularly `IdKind` crash messages in this change. In particular, for `RequireIdKind` in node_stack.h from using numeric IdKinds (e.g., 5 and 24) to something that will print `IdKind(<label>)` (this came up recently on #toolchain). While I'm in here, also doing some other tinkering: - Moving operators to be `friend` members, to reduce the extra templating now that the base types are templated. - Adjusts IntId diagnostics from `int [...]` to `int(...)` for consistency with other id printing. - Changes InstBlockId's label from "block" to "inst_block", since we have multiple blocks now. - Fixes StructTypeFieldsId to use "struct_type_fields" instead of "type_block" (from `TypeBlockId`) - Does some more adjustments from camelCase to snake_case for consistency |
||
|
|
3ba4997855 |
Canonicalize away bit width and embed small integers into IntIds (#4487)
The first change here is to canonicalize away bit width when tracking integers in our shared value store. This lets us have a more definitive model of "what is the mathematical value". It also frees us to use more efficient bit widths when available, such as bits inside the ID itself. For canonicalizing, we try to minimize the width adjustments and maximize the use of the SSO in APInt, and so we never shrink belowe 64-bits and grow in multiples of the word bit width in the implementation. We also canonicalize to the signed 2s compliment representation so we can represent negative numbers in an intuitive way. The canonicalizing requires getting the bit width out of the type and adjusting to it within the toolchain when doing any kind of math, and this PR updates various places to do that, as well as adding some convenience APIs to assist. Then we take advantage of the canonical form and embed small integers into the ID itself rather than allocating storage for them and referencing them with an index. This is especially helpful for the pervasive small integers such as the sizes of types, arrays, etc. Those no longer require indirection at all. Various short-cut APIs to take advantage of this have also been added. This PR improves lexing by about 5% when there are lots of `i32` types. --------- Co-authored-by: Dana Jansens <danakj@orodu.net> Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com> Co-authored-by: Jon Ross-Perkins <jperkins@google.com> |
||
|
|
4148161e24 |
Refactor value store code to use separate files. (#4477)
This is in anticipation of making the integer value store be customized heavily. I'd like to extract it from the common code when doing that, so first disentangling them here without any intended change in functionality or behavior to enable that. I've tried to update `#include`s to be as minimal as I can and added a few missing includes spotted in the process. I've split the test for value store to include what was easy focused on just the value store templates rather than the unified shared value stores. This might surface some opportunities for adding more tests, but for this PR, just doing the minimal restructuring. |
||
|
|
06f4eec91e |
Modify lex yaml output to elide FileStart/End in tests. (#4433)
Trying to make split file tests of lex functionality shorter and easier to read. numeric_literals.carbon in particular has an example of why I'm interested in this (at the bottom). This also switches from `[]` list format to `-` list format so that the trailing `]` is removed. Trimming comments in tokenized_buffer.h because (1) it feels like it's giving too much detail about what's printed, which has drifted slightly and (2) it also feels like it's trying to justify YAML output, when that's just what we're doing in general. --------- Co-authored-by: Geoff Romer <gromer@google.com> |
||
|
|
e68e54dae4 |
Issue a diagnostic if we try to parse a source file that is too large. (#4429)
Previously in an optimized build we'd produce bogus tokens, such as tokens with incorrect IdentifierIds, and in a debug build we would try to CHECK-fail -- but actually wouldn't, because we're incorrectly checking for `2 << bits` instead of `1 << bits`. I hit this while I was trying to do some profiling and was seeing some very strange diagnostics. The diagnostic is pointed at the first token that is beyond the limit to help people determine where to split their files. --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com> |
||
|
|
0db96ebc52 |
Stitch together adjacent comments using the indent. (#4397)
This is improving the comment production to produce fewer distinct comments. At present, comment processing uses strict prefix matching. It either expects `// ` (with a space) for valid comments, or just `//` (without a space) for invalid comments that lacked the space. As a consequence, the following would be three comments: ``` // Comment 1 // // // Comment 4 ``` This is because a 3-character prefix is used for valid comments. The prefix switches between lines 1 and 2, and again between lines 3 and 4, each resulting in a separate comment. For contrast, this is one comment because only a 2-character prefix is used: ``` //Comment 1 // // //Comment 4 ``` That's because all lines lack a suffix space. Additionally, with SIMD 16-byte boundaries, further splits can occur if processing needs to transition to non-SIMD. Here, I'm trying to just address all of this by: 1. Stitching together adjacent comments. Since a lexed comment starts at the `//` excluding the indent, the delta from the prior comment must be precisely the indent. 2. Adding support for switching from SIMD to non-SIMD on file boundaries. I considered trying to have a separate `//\n` prefix for SIMD processing of `// `, but I wasn't sure about the tradeoff of doing both at the same time (in particular, it'd require constructing a string for the different prefix), thus this stitch approach. This does mean multiple passes will be required for a typical long comment structure using blank comment lines to separate paragraphs (for performance reasons, I will recommend engineers not write comm... nevermind). --------- Co-authored-by: Chandler Carruth <chandlerc@gmail.com> |
||
|
|
1338f9e0ad |
Add tracking of lexed comments, with skeletal formatting. (#4385)
In order to format comments, it's helpful if they're tracked. This tracks them separately from tokens in order to avoid interfering with parse; it'd be inconvenient if comment tokens could show up in arbitrary locations, albeit possible to support. This additionally extracts out the TokenIterator support into a template in order to generally have it available for IndexBase types. I'm only adding it for CommentInfo, not sure if we'll want it elsewhere, but this structure still felt like a good fit. |
||
|
|
06344aeb7c |
Do some tactical inlining across lexer and parser. (#4307)
These are based on looking at our compilation benchmark and looking at function bodies that seem surprising to not get inlined. Note that this will have a bit more impact on x86 where function call overhead (especially due to pushing and popping registers) is a bit higher than Arm. For a recent AMD server, this makes parsing around 15% faster, and full "check" phase 5% faster. Benchmark results: ``` name old cpu/op new cpu/op delta BM_CompileAPIFileDenseDecls<Phase::Lex>/256 40.2µs ± 2% 37.8µs ± 1% -5.89% (p=0.000 n=19+17) BM_CompileAPIFileDenseDecls<Phase::Lex>/1024 190µs ± 2% 181µs ± 2% -4.93% (p=0.000 n=19+18) BM_CompileAPIFileDenseDecls<Phase::Lex>/4096 779µs ± 1% 745µs ± 2% -4.29% (p=0.000 n=19+19) BM_CompileAPIFileDenseDecls<Phase::Lex>/16384 3.44ms ± 1% 3.32ms ± 3% -3.32% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Lex>/65536 14.6ms ± 2% 14.3ms ± 3% -2.46% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Lex>/262144 66.7ms ± 2% 65.0ms ± 4% -2.52% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/256 85.7µs ± 2% 71.3µs ± 2% -16.77% (p=0.000 n=20+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/1024 421µs ± 2% 352µs ± 2% -16.38% (p=0.000 n=20+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/4096 1.71ms ± 2% 1.44ms ± 2% -15.89% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/16384 7.19ms ± 2% 6.10ms ± 2% -15.24% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/65536 29.8ms ± 2% 25.3ms ± 2% -14.91% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/262144 127ms ± 2% 109ms ± 2% -14.28% (p=0.000 n=20+20) BM_CompileAPIFileDenseDecls<Phase::Check>/256 785µs ± 1% 752µs ± 1% -4.13% (p=0.000 n=20+18) BM_CompileAPIFileDenseDecls<Phase::Check>/1024 1.71ms ± 1% 1.62ms ± 1% -5.17% (p=0.000 n=20+18) BM_CompileAPIFileDenseDecls<Phase::Check>/4096 5.28ms ± 1% 4.97ms ± 1% -6.04% (p=0.000 n=20+19) BM_CompileAPIFileDenseDecls<Phase::Check>/16384 20.2ms ± 1% 19.0ms ± 2% -5.98% (p=0.000 n=20+20) BM_CompileAPIFileDenseDecls<Phase::Check>/65536 83.8ms ± 1% 78.9ms ± 2% -5.84% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/262144 354ms ± 1% 335ms ± 1% -5.41% (p=0.000 n=19+20) ``` |
||
|
|
4845f40dff |
Switch CARBON_CHECK to a format string API (#4285)
This switches `DCHECK` and `FATAL` as well. The goal is to reduce the code size impact of these assertions so that we can keep more of them enabled. Currently, the largest cost I see from `CHECK` is not the actual check or the cold code itself, but actually the failure to inline trivial functions due to the presence of the cold code. This means that our goal isn't to reduce apparent code size in the final binary but the LLVM IR cost assessed for these routines in the inliner, which closely correlates with code size but is a bit different. As discussed in #4283, experimentation shows that a single function call with a minimal number of arguments is the lowest cost model for these. This is easily achieved with a format-string API that internally uses `llvm::formatv`. This PR is essentially the `CHECK` version of #4283. However, the check macros are substantially harder to make work with both format strings and streaming because they also take a condition. Also, unexpectedly, I was very successful at devising a regular expression based automated rewrite from the streaming to the format string form with only low 10s of manual fixes. This includes compacting strings broken up across lines, etc. Given how well that went, I've prepared this PR which just directly switches to the format string API and migrate everything to use it. One nice side-effect is that the format string approach ends up greatly simplifying the implementation here as well. This is ... *shockingly* effective. Parsing speeds up by more than 3% with just this change. And checking speeds up by **8%** with this change alone: ``` BM_CompileAPIFileDenseDecls<Phase::Parse>/256 86.3µs ± 1% 82.9µs ± 1% -3.94% (p=0.000 n=17+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/1024 431µs ± 1% 415µs ± 1% -3.76% (p=0.000 n=18+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/4096 1.77ms ± 1% 1.71ms ± 1% -3.18% (p=0.000 n=18+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/16384 7.44ms ± 1% 7.17ms ± 2% -3.56% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/65536 30.7ms ± 1% 29.7ms ± 1% -3.15% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/262144 131ms ± 1% 127ms ± 1% -2.81% (p=0.000 n=18+18) BM_CompileAPIFileDenseDecls<Phase::Check>/256 878µs ± 2% 800µs ± 1% -8.91% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/1024 1.88ms ± 2% 1.72ms ± 1% -8.56% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/4096 5.78ms ± 2% 5.28ms ± 1% -8.70% (p=0.000 n=20+18) BM_CompileAPIFileDenseDecls<Phase::Check>/16384 21.9ms ± 1% 20.1ms ± 1% -8.02% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Check>/65536 90.4ms ± 2% 83.1ms ± 1% -8.04% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/262144 381ms ± 2% 352ms ± 1% -7.79% (p=0.000 n=19+19) ``` --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk> Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com> |
||
|
|
d6b2fb1736 |
Add parse support for multiple requirements after where separated by and (#4298)
Follow on to #4275 that added `where` parse support. --------- Co-authored-by: Josh L <josh11b@users.noreply.github.com> Co-authored-by: Richard Smith <richard@metafoo.co.uk> |
||
|
|
c43fa3a8a5 |
Bit-pack the lexer's token info (#4270)
This makes each token info consist of 8 bytes of data: - 1 byte of the kind - 1 bit for whitespace tracking - 23 bits of payload - 32 bits for byte offset in the file This builds directly on representing the location of the token as a single 32-bit offset, now compressing the rest of the data into a single 32-bit bitfield. This adds some implementation limits: we can no longer lex more than 2^23 tokens in a single source file. Nor can we have more than 2^23 string literals, integer literals, real literals, or identifiers. Only the first of these is even close to an issue, and even then seems unlikely to ever be a problem in practice. The memory efficiency here is great and the motivating goal. But to make this work well, we also need to streamline how we create the tokens. Otherwise, all the bit fiddling can end up erasing our gains. This PR adds a number of APIs to manage creating and accessing the now significantly more complex storage of token infos to try and help with this. One big change required to simplify the writes here is to switch from computing whether a token has trailing space after-the-fact to pre-computing whether a token will have leading space. That lets us have the leading space information available immediately when forming the token, and avoids doing a single bit flip afterward. Another change that helps with this representation is to minimize the updating of groups after-the-fact. The code now tries to set the opening index directly when creating the closing token and only updates the opening group afterward. Because of the bit packing, this is a reduction of 0.5% of dynamic instructions in the compile benchmark, and has dramatic improvements for the grouping symbol focused benchmarks. All combined, this is a significant improvement on the lexer-focused benchmarks despite the added complexity, and a significant win on our compile time benchmarks due to both the lexer improvements and downstream memory density improvements: 5-12% reduction in lex time, growing larger as files get larger. About a 4.5% reduction in parse time, and even a 1-2% reduction in total check time. =D --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com> Co-authored-by: Geoff Romer <gromer@google.com> |
||
|
|
97e98bcc5a |
Shrink the lexer's token location and line data structures. (#4269)
First, this replaces the separate line index and column index in the token information with a single 32-bit byte offset of the token. This is then used to compute line and column numbers with a binary search of the line structure and then using that to compute the column within the line. In practice, this is _much_ more efficient: - Smaller token data structure. This will hopefully combine with a subsequent optimization PR that shrinks the token data structure still further. - Fewer stores to form each token's information in the tight hot loop of the lexer. - Less state to maintain while lexing, fewer computations while lexing. We only have to search to build the line and column information off the hot lexing path, and so this ends up being a significant win and shrinks some of the more significant data structures. Second, this shrinks the line start to a 32-bit integer and removes the line length. Our source buffer already ensures we only have 2 GiB of source with a nice diagnostic. I've just added a check to help document this in the lexer. The line length can be avoided in all of the cases it was being used, largely by looking at the next line's start and working from there. This also precipitated cleaning up some code that dated from when lines were only built during lexing rather than being pre-built, which resulted in nice simplifications. With this PR, I think it makes sense to re-name a bunch of methods on `TokenizedBuffer`, but to an extent that was already needed as these methods somewhat predate the more pervasive style conventions. I avoided that here to keep this PR focused on the implementation change, I'll create a subsequent PR to update the API to both better nomenclature and remove deviations from our conventions. There may also be a way to de-duplicate the binary search in the diagnostic location conversion and the main line accessor binary search, but it wasn't obvious to me that it would be a net savings, so left it alone for now. The performance impact of this varies quite a bit... The lexer's benchmark improves pretty consistent across the board on both x86 and Arm. For x86, where I have nice comparison tools, it appears 3% to 20% faster depending on the specific pattern. For Arm server CPUs at least it seems a much smaller but still an improvement. The overall compilation benchmarks however don't improve much with these changes alone on x86. Significant reduction in instruction count required for lexing, but the overall performance is bottlenecked elsewhere in the overall compilation it seems. However, on Arm, despite the more modest gains in special cases of lexing, this shows fairly consistent 1-2% improvements in overall lexing performance on our compilation benchmark. And the expectaiton is these improvements will compound with subsequent work to further compact our representation. --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com> |
||
|
|
f1190a4792 |
Add basic output of where memory is stored after a compile. (#4136)
The output is really basic, I'm just adding this to help track how memory is allocated. ``` --- filename: 'check/testdata/expr_category/in_place_tuple_init.carbon' source_: used_bytes: 8057 reserved_bytes: 8057 tokens_.allocator_: used_bytes: 0 reserved_bytes: 0 tokens_.token_infos_: used_bytes: 1040 reserved_bytes: 2032 (eliding) value_stores_.string_literals_.set_: used_bytes: 320 reserved_bytes: 320 Total: used_bytes: 20609 reserved_bytes: 29437 ... ``` --------- Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com> |
||
|
|
be94782eda | Remove unused member. (#4134) | ||
|
|
3c01ee69ed |
Move information on the token associated with a parse node from the .def file into the typed node. (#4001)
Instead of tracking the token associated with a parse node in the `.def` file macro, track it on the typed node instead. List the token as a field inside the node structure to show the order of the token relative to the other components of the grammar production, and to allow the token index to be accessed when the node is extracted. Remove the corresponding information from the `.def` file, leaving behind just a list of parse node kinds in the majority of cases. This also removes the checking of the token kind associated with a parse node in the case where the parse node has errors. Previously we had a flag on the node kind to indicate whether we should check this, but per [discord discussion](https://discord.com/channels/655572317891461132/655578254970716160/1246214418979881052), we have decided to remove this. --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com> |
||
|
|
bb117aea3a |
Add support for iN and uN for all suitable N. (#3868)
`i32` is retained as a special case for now, for bootstrapping purposes, and maps to `BuiltinIntType`, which is distinct from `Core.Int(32)`. This will be removed later once we support `Core.BigInt`. For now this provides both the `iN` types and also the builtins to support `Core.Int(N)`. The intent is that we'll change the `iN` support to rewrite to calls here when we do that for the other type literals and type keywords. No conversions between integer types are supported yet, and all literals are of type `i32`, so we can't actually form values of any of these new types. --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com> Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com> |
||
|
|
b5d28f2c4b | location -> loc abbreviation (#3826) | ||
|
|
6c458ffe7e |
Add import context for locations. (#3807)
As discussed around #3792, identify the import a diagnostic message came from prior to the diagnostic message itself. This occurs during location translation so that the logic can be central. I'd considered associating the parse node with ImportRef instructions, but I realized about halfway through that because I need to store the ImportDirectiveId on the ImportIR for cross-package imports, it's there for use in location translation without extra work. That saves a fair amount of stringing it through declarations, as well as an oddity where ImportRef instructions would have a node that didn't really represent them. |
||
|
|
0bd45f0d6b |
Rename DiagnosticLocationTranslator -> DiagnosticConverter (#3804)
Since the addition of TranslateArg, I don't think this type is going to go away (cutting a TODO). Refactoring names slightly to fit the current role, and adding const to ConvertLocation. |
||
|
|
5e88fe72c9 |
Switch remaining relational operator overloads to spaceship. (#3668)
This doesn't matter too much as these were expanded by the LLVM iterator facade, but eventually this should enable that facade to be a little less fancy and should also simplify the dispatch to directly use the three-way comparison. One case is a bit subtle and didn't have a comment so I added one to explain a bit what is going on there. --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com> |
||
|
|
0a06fceb5f |
Improve diagnosis of mismatched brackets. (#3282)
Move handling of mismatched brackets out of the main lexing loop into a separate pass that is only run if there are mismatched brackets This is done in preparation for using both lookahead and lookbehind to work out how to match brackets, and to get this code far away from the hot lexing loop. Fix bracket insertion location to be immediately after the token that we're inserting the bracket after, rather than potentially at the end of a comment. When there are open brackets at the end of the file, say that there are open brackets, not that there's a closing bracket without a matching opening bracket. |
||
|
|
d87fe8b532 |
Rename Carbon::StringLiteralId -> Carbon::StringLiteralValueId. (#3522)
We have `StringLiteral`s in multiple other `Carbon` sub-namespaces. Rename to a more specific name to avoid collisions. We should likely also rename `Carbon::IntId` -> `Carbon::IntValueId` and `Carbon::RealId` -> `Carbon::RealValueId`, but this collision is prioritized because it was blocking work on typed parse nodes which introduces a `Carbon::Parse::StringLiteralId`. |
||
|
|
c6bc2cbb3d |
Rename IndexBase -> IdBase, ComparableIndexBase -> IndexBase. (#3436)
This reflects how we're naming classes that derive from these classes, and matches usage for each existing `Id` and `Index` type, except: - `Parse::NodeId` previously inherited from `ComparableIndexBase`, and is no longer comparable. - `SemIR::MemberIndex` previously inherited from `IndexBase`, and is now comparable. Making `Parse::NodeId` non-comparable reflects that it's intended to be an opaque identifier for a node and that the ordering is an implementation detail rather than part of the intended public interface. `PostorderIterator` and `SiblingIterator` still rely on the numerical meaning of `NodeId`s, but that's OK since they're part of the node implementation. |
||
|
|
0db63ff17a |
Abbreviate Integer and FloatingPoint (#3435)
I was suggesting this because `FloatingPoint` is pretty long. `int` and `float` should be familiar abbreviations. `unsigned` should be familiar to developers too, but `UnsignedInt` still feels usefully clearer for the additional chars. |
||
|
|
eae630a3db |
Rename Lex::{Token,Line} -> Lex::{Token,Line}Index. (#3433)
As discussed [on discord](https://discord.com/channels/655572317891461132/655578254970716160/1178878128714678282) and today's toolchain discussion. |
||
|
|
3f208e27f9 |
Align on FileStart/FileEnd for naming. (#3428)
The lexer has been using EndOfFile form (stemming from EOF), parser went to FileEnd form. This consolidates on FileEnd form. |
||
|
|
35d15a390c |
Remove nodiscard uses. (#3418)
Per [#toolchain discussion](https://discord.com/channels/655572317891461132/655578254970716160/1176632520834560211) We'd at one point been trying to put `[[nodiscard]]` everywhere, but then we stopped because it had felt verbose without finding many issues (plus, people plain forgot to add it). Some history in #888. Since newer code gets added without it, we now have code like: ``` auto GetLineInfo(Line line) -> LineInfo&; [[nodiscard]] auto GetLineInfo(Line line) const -> const LineInfo&; auto AddLine(LineInfo info) -> Line; auto GetTokenInfo(Token token) -> TokenInfo&; [[nodiscard]] auto GetTokenInfo(Token token) const -> const TokenInfo&; auto AddToken(TokenInfo info) -> Token; [[nodiscard]] auto GetTokenPrintWidths(Token token) const -> PrintWidths; ``` Here, the lack of `[[nodiscard]]` doesn't mean anything: for example, `GetLineInfo` should not have its result discarded if it's called. But the mix could be confusing for readers. As a resolution, remove the attribute. `[[nodiscard]]` should be treated like other attributes going forward, which essentially means "avoid in general, add a comment to explain why the attribute is needed" rather than use-as-default. |
||
|
|
9154c6410e |
Support for reading source code from stdin and other unusual places. (#3416)
- Treat an input of `-` as meaning stdin. - Fix building of an llvm::MemoryBuffer from a non-regular file. - Do not enforce filename restrictions on non-regular files. - Do not invent an output file name based on the name of a non-regular file. --------- Co-authored-by: Chandler Carruth <chandlerc@gmail.com> |
||
|
|
cafcd88882 |
Split lexing logic and storage to separate files. (#3365)
Just reorganizing logic a little, trying to mirror the direction we've gone with check, lower, etc. That is, lex.h contains a function `Lex` that is used directly. Note, I'm avoiding making meaningful changes here. It could in theory still affect inlining in benchmarks, but I'm not seeing an impact. Before: ``` ------------------------------------------------------------------------------------------------------ Benchmark Time CPU Iterations UserCounters... ------------------------------------------------------------------------------------------------------ BM_ValidKeywords 2784949 ns 2784867 ns 249 bytes_per_second=214.452M/s tokens_per_second=35.9084M/s BM_ValidKeywordsAsRawIdentifiers 3222597 ns 3222551 ns 210 bytes_per_second=244.513M/s tokens_per_second=31.0313M/s BM_RawIdentifierFocus 5907836 ns 5907518 ns 103 bytes_per_second=264.873M/s tokens_per_second=16.9276M/s BM_ValidIdentifiers<1, 64, false> 6255128 ns 6254297 ns 105 bytes_per_second=235.488M/s tokens_per_second=15.989M/s BM_ValidIdentifiers<1, 1, true> |
||
|
|
3401eed8d8 |
Split IdentifierId and StringLiteralId from StringId (#3352)
Following up on discussion yesterday regarding this split. Note, I'm expecting #3341 to do IdentifierId -> NameId in SemIR. It might be worth adding NameId creation directly to StringStore if you're content with this setup though. |
||
|
|
c9458fe30a |
Add parse support for 'import', brush up 'package' a little. (#3347)
This detects ordering issues with the `package` and `import` statements. `library` is changed from package-specific to instead be generic between the two, since structurally it's non-specific. The next step would be to start exposing the results for the driver to make ordering decisions for checking. That'll involve further modifications to this code, but this felt like a reasonable change point because it's the extent of the parser enforcement, and still causes significant refactoring. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk> |
||
|
|
b2cfd5a8a8 |
Change StringLiteral to less frequently allocate a new string. (#3314)
Building on #3311, which started moving the result string into a `unique_ptr`, instead have `StringLiteral` use a `BumpPtrAllocator` to manage memory. But also, detect when a string is really trivial during `Lex` and, if so, return `contents_` directly. |
||
|
|
b08f8bf589 |
Remove unused LiteralStringId (#3321)
|
||
|
|
1b55ad86dd |
Extend SharedValueStores to SemIR (#3313)
Building on #3311, change SemIR to use the SharedValueStore. Since this removes hermeticity, raw output no longer prints ints, reals, and strings. TokenizedBuffer accessors are modified to return IDs because values are often passed through in semantics without needing to read them. I would've put SharedValueStores on Context, except for the GetArrayBoundValue convenience method. I felt awkward removing that, so it's on File, at least for now. That's then used by the formatter and Lower too. The flipside of this is that TokenizedBuffer has a SharedValueStores only for printing, so maybe that's similar enough to what File is doing. This doesn't start shifting other SemIR members to ValueStore, but that seems like a next step. |
||
|
|
d13f76e001 |
Add value store to be shared across compile stages. (#3311)
This updates lexing to use the data. I'll do checking separately, just to split changes. Note the ValueStore structure is also set up such that SemIR::File can use it for other fields. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk> |
||
|
|
6ba8712fbd |
Predetermine all the line splits in the lexer. (#3278)
## Summary ## Restructures the lexer to first scan the entire source text for newlines and create all the line structures needed. Doing this up-front makes it easy to produce an optimized version with minimal complexity. Currently, it leverages the system `memchr`, but even when expanded to handle more complex cases like CR+LF line endings, being isolated in this way will result in a significantly simpler implementation. This change improves the lexing of comment lines significantly by skipping their contents immediately. The overhead of the pre-scan is unmeasurable in all realistic benchmarks, and 10-30% in benchmarks consisting almost entirely of blank lines or comments. The improvement of comment lexing with average length comment lines mixed with code starts at 20% and goes up. Regressing blank line handling for non-empty comments seems like the right tradeoff (by far). ## Background and details ## One weak point in the lexer implementation were large runs of comments. While those aren't terribly common, they shouldn't present a hazard to the lexer performance. A bit more common is a pattern of comments like the following: ```carbon // Some method comment here. fn SomeMethodName(...) -> ...; // Some other method comment here. fn SomeOtherMethodName(...) -> ...; ``` Here, the lexer spends an inordinate amount of time getting from the `\n` after the first semicolon to the `fn` token. It has to skip a blank line, scan a line, find the `//` comment start, then scan to find the next `\n`, and then scan horizontal whitespace, etc. It is tempting to build a scanner *exactly* for this. In fact, I built one, and I can publish it in a PR if folks are interested in what it looks like. For x86-64, the PSHUFB trick used for scanning identifiers technically works. But it is *complicated*. Amazingly so. 150 lines of very subtle code with subtle performance pitfalls at ever turn. I felt very uncomfortable submitting it, but we can always go back to it. Nothing I've come up with quite matches it for sheer speed. However, most of the complexity and time is spent walking from a `//` to the end of the line. And *that* is something we can do very simply. In fact, there is a tuned function for that in libc: `memchr`. Using this we can build a very fast and much simpler scanner to split lines up-front. This PR uses that and a carefully crafted fast loop to first build up all the line info we need. Getting this to be as fast as possible required some other subtle changes, for example always creating a line structure that goes from the last `\n` and the end of the file. We then back up the EOF token to avoid surfacing this to users. The nice thing is the EOF token isn't part of any hot loop, and so this removes branches everywhere else at modest complexity. Once we have that, the rest of the lexer just needs to keep track of its current line in order to record column offsets. I've taken some care to try and optimize the lexer's usage of the line structures but there are more opportunities here I suspect. Combined, this gets much but not all of the performance of a huge SIMD scanner for newline-through-to-next-token. For extreme cases (100s of blank lines or empty comment lines between tokens) the holistic scanner is of course still much faster, but those don't seem nearly worth the cost. I was initially worried about the overhead of taking two passes over the source text, but in practice I've not been able to measure any appreciable cost to this with realistic source files. In some cases benchmarks with no newlines get *faster* because we use a much more efficient approach to fetch the source text into cache as a happenstance. And that in turn makes the byte-wise dispatched loop run faster as it stalls less. I'm particularly happy with this approach because it seems very clear how to extend this to support CR+LF, bare CR, and even complex mixtures without any significant speed cost. That wasn't at all true for the other approaches explored. I may try some further PRs to smooth out the last bits of slowness here, but already this is working excellent for me in practice. My 10mloc test case is down to 2.3s to lex. ## Raw benchmark data Using a tool that runs benchmarks before and after and analyzes the results, the following summarizes the CPU-time impact, each of these for lexing 100k tokens: ``` BM_ValidKeywords 2.57ms ± 1% 2.58ms ± 0% ~ (p=0.190 n=5+4) BM_ValidIdentifiers<1, 64, false> 9.24ms ± 4% 9.31ms ± 4% ~ (p=0.421 n=5+5) BM_ValidIdentifiers<1, 1, true> 3.05ms ± 4% 3.11ms ± 4% ~ (p=0.222 n=5+5) BM_ValidIdentifiers<3, 5, true> 10.9ms ± 0% 11.1ms ± 1% +1.76% (p=0.016 n=4+5) BM_ValidIdentifiers<3, 16, true> 11.1ms ± 7% 11.0ms ± 1% ~ (p=0.310 n=5+5) BM_ValidIdentifiers<12, 64, true> 12.2ms ± 1% 12.3ms ± 2% ~ (p=0.111 n=4+5) BM_HorizontalWhitespace/1 11.2ms ± 6% 11.1ms ± 2% ~ (p=0.841 n=5+5) BM_HorizontalWhitespace/4 12.0ms ± 3% 12.0ms ± 2% ~ (p=0.548 n=5+5) BM_HorizontalWhitespace/16 16.2ms ± 6% 15.9ms ± 8% ~ (p=0.690 n=5+5) BM_HorizontalWhitespace/64 27.7ms ± 3% 28.4ms ± 3% ~ (p=0.151 n=5+5) BM_HorizontalWhitespace/128 44.3ms ± 1% 45.6ms ± 6% +3.15% (p=0.032 n=5+5) BM_RandomSource 7.75ms ± 2% 7.72ms ± 1% ~ (p=1.000 n=5+5) BM_BlankLines/1 11.7ms ± 1% 12.1ms ± 1% +3.46% (p=0.008 n=5+5) BM_BlankLines/4 14.0ms ± 2% 15.2ms ± 3% +8.12% (p=0.008 n=5+5) BM_BlankLines/16 23.5ms ± 2% 31.1ms ± 4% +32.26% (p=0.008 n=5+5) BM_BlankLines/64 75.3ms ± 1% 81.2ms ± 3% +7.83% (p=0.008 n=5+5) BM_BlankLines/128 133ms ± 3% 150ms ± 2% +12.74% (p=0.008 n=5+5) BM_CommentLines/1/0/0 13.1ms ± 0% 13.7ms ± 1% +5.11% (p=0.008 n=5+5) BM_CommentLines/4/0/0 16.6ms ± 1% 18.2ms ± 4% +9.56% (p=0.008 n=5+5) BM_CommentLines/128/0/0 169ms ± 4% 182ms ± 1% +7.24% (p=0.008 n=5+5) BM_CommentLines/1/30/0 18.7ms ± 5% 14.1ms ± 0% -24.84% (p=0.008 n=5+5) BM_CommentLines/4/30/0 36.5ms ± 6% 20.6ms ± 3% -43.59% (p=0.008 n=5+5) BM_CommentLines/128/30/0 525ms ± 4% 198ms ± 1% -62.38% (p=0.008 n=5+5) BM_CommentLines/1/70/0 23.4ms ± 6% 14.7ms ± 2% -37.15% (p=0.008 n=5+5) BM_CommentLines/4/70/0 53.3ms ± 7% 22.4ms ± 4% -57.99% (p=0.008 n=5+5) BM_CommentLines/128/70/0 1.05s ± 4% 0.21s ± 2% -80.31% (p=0.008 n=5+5) BM_CommentLines/1/0/2 14.1ms ± 6% 14.3ms ± 1% ~ (p=0.151 n=5+5) BM_CommentLines/4/0/2 19.4ms ± 5% 20.1ms ± 1% ~ (p=0.151 n=5+5) BM_CommentLines/128/0/2 238ms ± 8% 229ms ± 0% ~ (p=0.151 n=5+5) BM_CommentLines/1/30/2 19.2ms ± 7% 14.6ms ± 1% -23.87% (p=0.008 n=5+5) BM_CommentLines/4/30/2 40.3ms ±13% 22.3ms ± 4% -44.63% (p=0.008 n=5+5) BM_CommentLines/128/30/2 568ms ± 7% 254ms ± 3% -55.28% (p=0.008 n=5+5) BM_CommentLines/1/70/2 23.3ms ± 1% 15.0ms ± 3% -35.61% (p=0.016 n=4+5) BM_CommentLines/4/70/2 57.2ms ± 9% 24.1ms ± 2% -57.81% (p=0.008 n=5+5) BM_CommentLines/128/70/2 1.07s ± 0% 0.26s ± 2% -75.51% (p=0.016 n=4+5) BM_CommentLines/1/0/8 15.9ms ± 7% 16.0ms ± 1% ~ (p=0.151 n=5+5) BM_CommentLines/4/0/8 24.2ms ± 6% 27.9ms ± 2% +15.36% (p=0.008 n=5+5) BM_CommentLines/128/0/8 386ms ± 4% 445ms ± 1% +15.28% (p=0.008 n=5+5) BM_CommentLines/1/30/8 20.6ms ± 5% 16.3ms ± 1% -20.95% (p=0.008 n=5+5) BM_CommentLines/4/30/8 45.3ms ± 6% 30.3ms ± 3% -32.98% (p=0.008 n=5+5) BM_CommentLines/128/30/8 699ms ± 3% 477ms ± 3% -31.83% (p=0.008 n=5+5) BM_CommentLines/1/70/8 25.7ms ± 5% 16.8ms ± 2% -34.67% (p=0.008 n=5+5) BM_CommentLines/4/70/8 62.0ms ± 4% 31.6ms ± 2% -49.10% (p=0.008 n=5+5) BM_CommentLines/128/70/8 1.20s ± 2% 0.48s ± 4% -59.60% (p=0.008 n=5+5) ``` The horizontal whitespace benchmark (and all of the non-line-oriented ones) are noisier than they appear here but do show some improvements (surprisingly). My guess is that it has a lot to do with system load, as the advantage is that we're using a vectorized loop to scan the text first and then doing the byte-dispatched loop. So when the cache is a bit slower to populate, the vectorized version starts to be faster. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk> Co-authored-by: josh11b <josh11b@users.noreply.github.com> |
||
|
|
6833652ffa |
Misc fixes to toolchain (#3261)
Was reading through the code and saw some small improvements. |
||
|
|
0b340a2ed2 |
Update parse tree yaml for multi-file. (#3215)
Building on #3214, updates parse tree yaml to be: ``` - filename: name parse_tree: [ ... ] ``` |
||
|
|
87d4c2dfc6 | Improve testing and handling of unsigned APInt values (#3202) | ||
|
|
ec182fb00d |
Rename lexer dir to lex (#3179)
Continuing with #3070. Just a dir and file rename (only prefix change is lexer_file_test). Everything in the lex dir should be marked as a move. Note, I think this closes #3070. There may still be further cleanup later, but the organizational changes suggested there are being completed. --------- Co-authored-by: Chandler Carruth <chandlerc@gmail.com> |