sourcepawn
sourcepawn copied to clipboard
WIP: Generate an IR (aka int64 part 1/4).
The compiler has come a long way, but we still can't do int64, because it requires a non-cell storage size. There's no (sane) way to express conversions between int32 and int64 within the AST, because we have no uniform way of inserting conversion nodes. This is already a deep problem that has been hacked around for operator overloads and property accessors, and it doesn't scale.
The solution is obvious: transform the AST into an IR. That's what we should have done from the beginning but didn't. Unfortunately it requires a lot of refactoring and a ton of boilerplate. So far, I have most of the boilerplate done, but the refactoring is only halfway there. CodeGenerator has not been ported to the IR yet.
Once this gigantic patch is done, we'll have the following changes:
struct valuewill be eliminated, and good riddance.- The AST will be immutable after parsing.
- The semantic analysis phase will output a new IR tree.
- CodeGenerator will generate off the IR instead. Since the IR is a transformation of the AST, I'm expecting minimal changes to the end result.
I'm calling this part 1 of 4, since roughly the steps toward int64 are:
- Introduce an IR.
- Eliminate matchtag and have TypeChecker::Coerce insert conversion nodes.
- Refactor the VM to support wide ALU ops (either stack-based ops or make PRI/ALT dynamically sized).
- Introduce new 64-bit wide types.
This is great work! There has been some demand for an int64 type for quite a long time, especially in the awakening of 64-bit SourceMod as a result of Team Fortress 2. Kenzzer's MemoryPointer should be ideal at this current moment, but in the long run this an int64 type is something I am particularly interested in looking into.
Out of curiosity, will a SourcePawn VM refactor also feature introducing x86-64 JIT support, in particular for 64-bit srcds servers? Would a potential double type also be introduced?
P.S. thank you for reminding me that I need to learn more about how compilers work haha
The VM already supports x86_64 right? Do you mean a JIT? If so, no plans for one since I don't think it's as important as language feature support. But that could change, especially with motivating evidence.
Yeah I meant the JIT - apologies, I had just woken up. That's fair, I can't give much measurements at the moment as I haven't played with 64-bit TF2 servers a whole lot yet.
last time we tested x64 without JIT, it had significant performance regressions comparing to x32 with JIT. Therefore we reverted our servers back to 32-bit srcds.
would likely need someone with active player population to do vprofs on both versions
I have run a profiler on both as seen here:
https://github.com/alliedmodders/sourcepawn/issues/965
We have a single 2fort server on x64, it uses about 20% more cpu with the heaviest plugin being SMAC.
This is probably acceptable for all but 100 slot servers, which only 1 person has anymore (not us).
It could also be worse for custom game modes that heavier users of sourcepawn though.
Extremely likely X86 jit won't survive this refactoring as it's internally incompatible with the planned opcode changes. It'll be frozen for compatibility and used on old binaries only. It's possible I might find a route to refactor pri/alt but I doubt it as it'd be a nightmare to maintain.
I'd be open to a new JIT, likely 64bit only, but showing performance decrease isn't enough. It needs to be shown that (1) the issue is not reasonably addressable in the plugin itself (dispatch costs are huge and plugin is not doing dumb stuff) and (2) the issue is actually meaningful (playability of a game is affected). A crude analogy: A car's max speed dropping by 50% doesn't matter if the speed is still 100mph and is driving on 60mph roads.
I get the concern around performance, but wishcasting or anecdotes don't change the calculus: the language won't move forward without breakage. So it'll break, and then we see where things stand.
Just to be clear we're personally fine the lack of JIT right now, though we wouldn't say no if you were to make one.
As you explained, as long as the cpu usage on 1 core is less than 100%, it doesn't matter if sourcemod is using 20% vs 90%. This would only be an issue on 100 slot servers which only 1 youtuber is able to fill.
However we're fortunate enough to be running on newish cpus and others might not be so fortunate. We're also not using the most demanding plugins, so I can't speak for everyone.
Extremely likely X86 jit won't survive this refactoring as it's internally incompatible with the planned opcode changes. It'll be frozen for compatibility and used on old binaries only. It's possible I might find a route to refactor pri/alt but I doubt it as it'd be a nightmare to maintain. I'd be open to a new JIT, likely 64bit only, but showing performance decrease isn't enough. It needs to be shown that (1) the issue is not reasonably addressable in the plugin itself (dispatch costs are huge and plugin is not doing dumb stuff) and (2) the issue is actually meaningful (playability of a game is affected). A crude analogy: A car's max speed dropping by 50% doesn't matter if the speed is still 100mph and is driving on 60mph roads. I get the concern around performance, but wishcasting or anecdotes don't change the calculus: the language won't move forward without breakage. So it'll break, and then we see where things stand.
Our community won't be affected as we have budget, others not so much.
Gotta keep in mind that many people run multiple servers on single machine, therefore the cpu usage increase stacks up. Add onto that, a lot of people running srcds on cheap VDS/VPSes nowdays too.
Anyway, worrying about performance imo should be done after any major changes SM devs have planned for sourcepawn are done. Premature optimization is the root of all evil as they say 😄
And even then without JIT, we all can always try to brainstorm some optimizations for VM itself!
Making slow progress. There are some things that are way, way easier to express in the IR. Operator overloads are so much nicer, and in general individual nodes are much simpler. But other things are a somewhat harder. Like, a naive += can double-bind the lvalue, which could lead to obscure bugs. For example "x[crab()] += 5" could call "crab()" twice instead of once, so we need new kinds of nodes that weren't previously in the AST. There's lots of little details like this to work through.