Replies: 17 comments 19 replies
-
|
Source: https://github.com/Mond45/cs6120_tasks/tree/master/task6 What I DoI implemented the converter into and out of SSA form in Rust. The Most of the work happens in the variable renaming step, where both variables and instruction arguments are renamed. Here, The out-of-SSA converter is much more straightforward. It simply traverses every instruction: a set for variable TestingFor the into-SSA converter, I first verify that the resulting IR is indeed in SSA form using the For out-of-SSA, I follow a similar process. Initial testing on the example tests revealed an issue in my early implementation, which only handled The Hardest PartThe hardest part was implementing the into-SSA algorithm, particularly the variable renaming step. I spent considerable time figuring out exactly where to insert Handling undefined variables and function arguments also required careful thought, but I'm quite satisfied with my final solution. The out-of-SSA conversion turned out to be less trivial than I first expected as well, especially after dealing with the edge cases revealed by the example tests. PerformanceHere are the slowdown statistics across all Bril benchmark programs, measured as the ratio of dynamic instruction counts for programs in SSA form compared to the baseline: Since my out-of-SSA implementation does not remove any instructions, the slowdown remains the same. ConclusionI believe my work deserves a Michelin star, given its non-trivial implementation and thorough testing. I'm quite satisfied of the resulting implementation and learned a great deal from this task. I like that we got to reuse parts of previous tasks and it was satisfying to finally understand some concepts, such as realizing that we can simply handle inserting |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
|
Code: https://github.com/maheshejs/cs6120pr SSA I added 7 passes to my compiler pipeline, broken into three stages: SSA preparation, going into SSA, and coming out of SSA. ;; Compiler pipeline
(define compile-pipeline
(compose
flatten-program ; JSON
bury-dead ; DCE
optimize-values ; LVN
remove-phis ; SSA-OUT
hoist-phis ; SSA-IN (Pizlo’s form)
rename-variables ; SSA-IN
place-phis ; SSA-IN
insert-undefs ; SSA-IN prep
decorate-defines ; SSA-IN prep
fake-entry ; SSA-IN prep
remove-unused-instructions ; CFG cleanup
decorate-flow-graph ; CFG construction
expose-basic-blocks ; BB construction
))
;; Parse input JSON, apply pipeline, and output result
(define program
(compile-pipeline (read-json (current-input-port))))SSA Preparation Into SSA
Once in classic SSA form, I convert to Pizlo’s form with Out of SSA
Later passes (DCE and LVN) clean up Testing
Results Total: 56 | Improved: 0 (0.0%) | Worsened: 56 (100.0%) | Unchanged: 0 (0.0%)
The SSA roundtrip introduces overhead as expected, though the global LVN + DCE keeps it around 0.8× on average, which corresponds to a 25% overhead. Hardest Part Conclusion |
Beta Was this translation helpful? Give feedback.
-
Team MembersNing Wang (nw366), Jiale Lao (jl4492) Source Codehttps://github.com/NingWang0123/cs6120/tree/main/assa6 Implementations
Testing and Visualization (Generative AI part)All 62 benchmark programs from
We use gpt-5 to generate the visualization codes in From this figure, we can see the static instruction overhead for each program, ordered from low overhead to high overhead with different colors to represent different levels of overheads. As we can see, the overhead could differ significantly across different programs (e.g., it introduces nearly 700 more instructions for dayofweek, for other programs it is typically less than 100 instructions). From this figure, we can see the statistics and the summary. All 62 programs pass the SSA form verification and round-trip testing. For the overhead, the total original instruction number is 2148 and we introduce 1789 overhead instruction, with an overall overhead of 83.29%. This meas, we almost double each program after SSA round-trip. The hardest partCompared to prior tasks, this implementation is more complex and we need to understand the algorithm carefully and then implement things correctly. But the good thing is that, it is easier to test and evaluate (for some of prior works, we need manual checking). The hardest part is thinking how to reduce the overhead. The number of instruction is a good metric for measuaing overhead, but given that different instructions have different overheads, maybe we still need to measure the end-to-end execution time? Michelin StarI think we deserve a star, given that we implement all the functions, good testing, and good visualization. |
Beta Was this translation helpful? Give feedback.
-
|
This time I write to_ssa.py to transform a regular Bril program into SSA form. I divide the whole task into 9 parts with the help of Chatgpt-5 and implement each of them and finally get my program running. The parts are listed below: |
Beta Was this translation helpful? Give feedback.
-
|
Implementation: Link What I didI built an into SSA pass in Pizlo’s set/get form using the dominator frontier algorithm. First I constructed CFGs, dominators, idom tree, and dominance frontiers. For each variable, I place get at DF sites, treat function params as defs at entry, then do stack-based renaming over the dom-tree. After renaming, I split phi across edges by inserting set in each predecessor; missing defs use a typed undef temp. An out of SSA pass lowers get x->id x, x, and leaves set as a valid effect. EvaluationI wired turnt to run the first 30 core benchmarks in bril and added runners that infer # ARGS: or type defaults. This caught type pitfalls (e.g., eq on non-ints) which I fixed by inferring base types and annotating generated get/undef. I also made some plots: Top files by static delta Static overhead CDF of static delta
Hardest partGetting SSA construction robust across all Bril tests (not just toy examples) was way harder than expected. My first version worked on a small hand-written case, but it blew up on real programs due to three intertwined issues:
Even after these, corner cases kept surfacing. fallthrough edges, placing set before the terminator, and ensuring renaming never rewired set’s shadow name incorrectly. I iterated with turnt on the full test suite (well I couldn't get it through the entire core benchmarks, there's still one failing), and when I got stuck I leaned on GPT to pinpoint where the algorithmic intent didn’t match my implementation details and it is being very helpful. Gen AI usageI used GPT in this homework.
|
Beta Was this translation helpful? Give feedback.
-
|
Team Members Converting to SSAFor converting to SSA, we split our code into 3 parts: collecting variable definitions, inserting the phi nodes, and renaming the variables. Converting from SSAThis part of the assignment involves converting from SSA back into regular form. Here, we are replacing each TestingFor testing, we first manually verified via random sampling that the test cases with and without SSA had the same results. Then, we measured the overhead of adding SSA by comparing the static overhead of the generated bril files. Our results are shown below: Hardest PartThe hardest part was definitely implementing the converting to SSA, as well as debugging existing utility files that were broken. Converting to SSA was difficult purely because we had a lot of different parts to implement, which made debugging difficult. Debugging was also difficult because this project built off of some previous sections we implemented, and we found small bugs in those programs as well. Michelin StarWe believe we deserve a michilin star for implementing SSA, as well as integrating our program with previous tasks. |
Beta Was this translation helpful? Give feedback.
-
|
Team Members Link to Implementation Implementation Overview
For phi node placement we used the dominance-frontier from the last assignment + a single dominator-tree DFS rename. When a use can be reached without a prior def, we materialize a typed undef in that predecessor and wire it into the successor’s phi node input to match Bril’s SSA extension semantics. Testing Methodology
For dynamic and static overhead:
Hardest Part
Results/Output
Self-Assessment GAI Tools Disclaimer |
Beta Was this translation helpful? Give feedback.
-
|
source: https://codeberg.org/pedropontesgarcia/bril/src/branch/feat We never use generative AI on these assignments. what we did We implemented into and out of SSA functions. We used the Pizlo form of 'upsilon' / 'phi' nodes, and the dominance-frontier based into SSA function. Since the Pizlo form already 'pushes' phi nodes into the parent blocks, the out of SSA algorithm just replaces all This image shows the ratio of instructions executed for a roundtrip vs. baseline. Pretty bad, as expected: This image shows the ratio of instructions executed for the SSA form vs. baseline. Much worse, as expected: This image shows the ratio of instructions executed for a roundtrip vs. the reference SSA roundtrip. We're generally doing better, without DCE: verification We used two brench test suites to test our implementation. The first tests:
It also produces instruction execution count for the reference Python The second uses the Both use the entire hardest part The into SSA was definitely the hardest part. We used the Pizlo format, which complicated things further. We spent quite a bit of time writing and testing the code; and chasing down bugs as we encountered them. One bug that took an especially long time to fix was in the We also worked together in-person and talked through the problems we were encountering. Luckily, the out of SSA was made pretty easy by the Pizlo format. michelin star? Yes! This assignment was hard, and for relative novices in Rust (plus despite a really busy two weeks for the both of us, with multiple exams shared between the two of us), we were able to get something that works decently well (and do the more complicated dominance-frontier based version as well). Some refactoring can be done further down the road for efficiency, but the implementation is sound, and that's the key thing. We also added the ability to print our CFGs with GraphViz (see |
Beta Was this translation helpful? Give feedback.
-
|
Source: HERE Implementation: I implemented the from SSA form first, then the to SSA in its naive form. I intended to do the dominance frontier version, but the helper functions for from SSA form and to SSA were more complex than I initially realized and I was unable to get to it. Hardest Part: Definitely the to SSA form, I had to really work hard to rename everything, since my pattern matching didn't allow easy (non-tedious) access of the arguments of a function Michelin Star: I do not claim a michelin star for this assignment, as I was unable to verify my result as I ran out of time. |
Beta Was this translation helpful? Give feedback.
-
|
Amanda Wang @arw274 and I worked together on this. What we didWe implemented an algorithm to convert Bril to SSA (with For the out-of-ssa pass, we implemented a very naive algorithm which simply introduced "shadow" variables explicitly and replaced all Analysis and testingWe used
The hardest partThe hardest part of this assignment for us was probably the phi placement component of converting to SSA, since it's the one that most differed from our expectations in how many edge cases there would be and what they would look like (and this is why we switched to the Braun et al approach); I at least was not expecting the large number of useless phi instructions it would place and how we would have to use Michelin starWe believe we deserve a Michelin star for an intuitive and nonstandard implementation of the to-SSA transformation, sidestepping multiple issues by mixing the Braun and Cytron algorithms. |
Beta Was this translation helpful? Give feedback.
-
|
https://github.com/jeffreyqdd/rust_bril/ What I DidI implemented pruned SSA, which only inserts phi nodes in the dominance frontiers where the target variable is live. To ensure safety, my compiler first runs an initialization analysis (a forward dataflow analysis using the worklist algorithm) to check that used variables are initialized on all incoming control branches. This actually caught a bug in the Afterward, my compiler performs φ-insertion and SSA renaming, and then runs Dead Code Elimination (DCE) and Local Value Numbering (LVN) on the SSA form. However, these optimizations did not eliminate as much redundancy as I expected. I believe this is because I have not yet implemented logic to properly handle φ-nodes within these passes. After my compiler inserts and renames the phi nodes, it runs DCE and LVN on the SSA form. However, these optimizations did not eliminate as much redundancy as I expected. I think this has three major reasons.
Analysis and TestingI wrote a short script that runs my compiler on every benchmark, piping the intermediate output into the is_ssa.py utility. I also collected static and dynamic instruction counts on all of the benchmarks. I also compared my SSA round-trip, SSA + DCE, SSA + LVN + DCE, with the baseline outputs to ensure that the program still behaves as intended.
Hardest partThe hardest part of this assignment was, by far, the phi placement. It was especially tricky to deal with the following case. I added a new basic block at the top of every function (could be empty), but this would allow me to push the phi function when converting out of SSA. Dealing with function arguments was also really tricky. Michelin StarI think I deserve a Michelin star for the time I put into this project to get the phi nodes working correctly. It's a shame I wasn't able to improve DCE and LVN to get a better speedup. I know it's possible, but I just genuinely don't have the time to work on this project anymore. |
Beta Was this translation helpful? Give feedback.
-
|
The bulk of the additions can be found here. Be warned, this one is particularly not pretty. SummaryI implemented functions to go to ssa and come out of ssa based using an algorithm based on dominators. I tested a few difficult cases on hand crafted benchmarks and then evaluated the transformation on every core bril benchmark. It leaves unchanged the behavior of all of the benchmarks, however the performance hit does cause the already long running delannoy benchmark to timeout a lot of the time (I'm pretty sure this isn't due to a semantics change though). I used Some Implementation ChallengesI'm mirroring @jeffreyqdd right above me, but I have to concur that dealing with the entry blocks took a lot of thought. Most of the time EvaluationAs mentioned earlier,
The round trip SSA transformation seemed to incur around a 20% penalty to dynamic instruction count. Many of these instructions end up being Comparing converting SSA to SSA + DCE, there is a huge, around 90% of baseline performance improvement. This suggests running DCE to remove extra A big outlier in these test cases was the michelin star(s)I managed a functional implementation and insightful (to me at least) evaluation of that implementation. I think both the implementation and evaluation are a little rough around the edges, but are still interesting and show some good work. However, this is also in the ~1-2 days late range so I would lean towards 0 stars (idk, is half a star a thing?) because of that. |
Beta Was this translation helpful? Give feedback.
-
ImplementationI implemented the very basic “into SSA” and “out of SSA” transformations for Bril functions in TestingI created Michelin StarI’ll claim ? Michelin star for completing this task. Sorry for the delay… |
Beta Was this translation helpful? Give feedback.
-
Team & CodeJake Hyun | Codebase What I DidI implemented bidirectional SSA transformations for Bril using the set/get (upsilon/phi) representation. The Testing and ResultsI tested extensively using round-trip validation: original → SSA → back to standard, comparing outputs with Results: All 122 programs pass correctness validation with very low overhead - only 13-14% increase in instruction count, which I'm satisfied with. Refer to results.json for comprehensive results. Hardest PartThe most challenging aspect was adapting the traditional dominance-frontier algorithm to work with set/get semantics. Ad-hoc patching the original algorithm to the set/get form ended up being a rabbit hole - figuring out how to split phi-nodes across CFG edges, handling terminator vs. fall-through insertion points, and managing shadow variable naming became quite complex. After iterating extensively on the benchmark suite, I ended up with something that may not be pretty but gets the job done. The specific breaking point was Generative AII used generative AI to help clarify SSA concepts and algorithmic details when I got stuck on specific implementation challenges. It was particularly helpful for understanding the nuances of dominance frontiers and variable renaming in SSA form. I also used AI assistance to streamline my testing workflow, helping design the comprehensive test harness that validates correctness and measures performance across all benchmark suites. Michelin StarI put significant effort into this implementation, achieving 100% success rate across a comprehensive test suite with remarkably low overhead, solving challenging edge cases that trip up many SSA implementations, and building robust testing infrastructure that validates both correctness and performance on all benchmarks. However, I did submit this work 2 days late, so I understand if that affects the evaluation. |
Beta Was this translation helpful? Give feedback.
-
|
Codebase: https://github.com/Nikil-Shyamsunder/compilers-cs6120 Section 1: What I Did For this assignment, I implemented the “into SSA” transformation for Bril using the dominance frontier–based algorithm, which is the more challenging version of the SSA construction approach. To start, I realized that my dominance frontier computation from previous work was not entirely correct. Because of that, I significantly reworked my dominance and frontier calculations, rewriting much of that logic to make them more robust and consistent with the SSA algorithm’s needs. Once I had a correct dominator and frontier computation, I implemented the into SSA pass. The algorithm first identifies where phi-like merges (in Bril’s case, implemented as get/set pairs) need to occur using dominance frontiers. Then, it systematically renames variables by walking the dominator tree, ensuring every variable definition gets a unique name version, and inserting get/set instructions as needed to maintain SSA semantics. After finishing that, I implemented the out of SSA transformation, which was comparatively much easier. This pass effectively reverses the SSA process by analyzing the set operations to recover the correct variable mappings, normalizing names, and removing SSA-only instructions (get, set, undef). This restores the program to a valid, non-SSA form while preserving semantics. Section 2: Testing For testing, I used the core benchmark suite from the Bril repository. My first step was to run the provided Python script is_ssa on all the outputs of my into SSA pass to verify that they were valid SSA programs using a shell script I wrote. It created a valid ssa form for all the core benchmarks. Once that passed, I ran end-to-end benchmarks comparing the baseline program, the SSA intermediate form, and the round-tripped (baseline to SSA back to non-SSA) version. I verified that all three versions produced identical functional results and also collected data on their total dynamic instruction counts using brench, and all passed.
Overhead as compared to baseline Section 3: Hardest Part It turned out that the root cause was indeed in the dominance frontier computation, and once I fixed that, everything else started to fall into place. Another tricky bug was how I was inserting set instructions: I always placed them right before the terminator instruction, forgetting that some fall-through blocks don’t have explicit terminators. Debugging those subtle control-flow edge cases taught me a lot about being more systematic and careful in maintaining larger compiler passes as the project grows. Gen AI I did use Gen AI across this project, primarily to help me reason about and partially prototype parts of the dominance frontier logic in C++. However, it struggled to understand how my data structures interacted, and its suggested code was not functionally correct for my setup. I ended up engineering most of it myself, using AI more as a sounding board than as a direct code generator. I also used it to write my shell script to call is_ssa on all the core benchmarks after being turned into ssa form and to write the specific data analyses I wanted from the csv file generated by brench. ChatGPT did these flawlessly. Additionally, I referenced the official Bril reference implementation of dominators to compare outputs and verify correctness. While I’m not thrilled that I had to refer to the reference code, doing so allowed me to confirm that this was the root cause of a bunch of errors I had by comparing the outputs from the reference implementation and mine and seeing they were inconsistent. Michelin Star This week’s case is admittedly a bit shaky. I struggled early due to incorrect dominance logic from a previous assignment, which slowed me down. However, I ultimately got the SSA transformation working correctly, and I’m proud of how I methodically debugged and fixed the issues, so maybe that deserves a star. |
Beta Was this translation helpful? Give feedback.
-
|
open source: https://github.com/Smubge/cs6120-L2/tree/L6 Cynthia Shao and Jonathan Brown worked together on this task. SummaryWe implemented the SSA using the Upsilon/Phi Variant as well as the conversion out of SSA. We verified correctness using the is_ssa.py script as well as checked that the programs would do the same thing when converted to SSA and back again. BenchmarkWe used the core benchmarks within Bril, and we tested for correctness after both passes - into SSA and out of SSA. Dynamic instruction count overall increased by 22.02% after the SSA pass, and in the figure below we can see that only 6 programs had a 40% increase in dynamic instruction count, but for 14 programs we were under 10% dynamic instruction difference. SSA_InFor converting into SSA, I first followed the pseudocode for dominance frontiers, and I implemented by inserting phi instructions. I then realized that phi instructions were deprecated, and modified that implementation into get and set instructions. ##SSA_out TestingFor testing, we first implemented our SSA with the phi instructions, and then realized that phi instructions in bril were deprecated, and would not work with brili. Due to this, we changed to the Upsilon/Phi Variant, utilizing the get/set instructions. For converting to SSA, we tested by first using the is_ssa.py script on the to_ssa example tests, and verifying that the functionality of the snapshot tests were the same. This was super helpful in the initial debugging, as the benchmark programs are oftentimes very long and hard to debug. We then tested on the core_benchmarks, adding a toml env for both to_ssa and out_ssa. We tested against brili for functionality, and we created .prof files to see the dynamic instruction count. This caught the majority of bugs, especially the ones where the get and set variables weren’t recognizing the back edges of a loop header. For the output, we tested by adding the ssa_out function after the conversion to SSA code, and testing it alongside utilizing the testing code between the ssa and the normal bril code. We also compared the number of instructions of each of them, and used this to view how many more instructions SSA would add to a bril file. Furthermore, we realized that because SSA_out converted the set and get functions to identity functions, that this would result in the same amount of instructions for ssa_out’s output as it would with the ssa_in output. Hardest PartThere were many hard parts to this task, and honestly we couldn’t really pinpoint one. One tough point was the realization that phi instructions were deprecated. We believe that through testing should always be the basis of implementation, and the inability to test incrementally between passes was the main motivator of switching to the get/set method. Another difficult part was figuring out where to insert undefined variables, as well as what happens when the first basic block of a function is also a loop header. These two problems went hand in hand, and the eventual solution was that we had to initialize sets before that loop header label. Additionally, we ran into a lot of naming issues. For example, things that we did as hacks before was a problem - naming blocks to be unique because we didn’t want to separate our data structures to be function based was an issue when we wanted to create new branches with SSA form. This was solved by a refactoring of all our data structures to be a mapping of functions to their respective basic blocks, preds, cfgs, and dominator sets, and turning all our construction logic into proper constructors. Self AssessmentWe sincerely apologize for the late submission, we experienced delays due to prelims, travel, and health challenges. We believe we deserve a Michelin star because we are proud of our work! We brainstormed an implementation of SSA, and thoroughly tested it. We tested thoroughly using both hand implemented benchmarks, against the core bril benchmark suite, and utilized the algorithms provided(is_ssa.py) to ensure tested correctness. Overall, we collaborated on the SSA’s design decisions and ensured correctness. We also practiced good team coding practices with git, and learned a lot about SSA, its added load to instructions, and the Phi/Upsilon Variant as a whole! We believe we deserve a Michelin star for the tenacity and persistence put into the task. |
Beta Was this translation helpful? Give feedback.

















Uh oh!
There was an error while loading. Please reload this page.
-
Beta Was this translation helpful? Give feedback.
All reactions