ID:1304890
 
http://tobba.pw/disasm.php

Put in any code and it will compile and disassemble it, for optimization or educational purposes (actually do whatever the hell you want)

Note that it only supports ~120 opcodes right now, most ones used only in one place like the ones for the built-in icon procs are not supported at this time

Current problems:
* Switch statements dont always decode properly, and the whole thing sometimes dies in a fire on them
* Globals come out with their number instead of their name (need to write code to handle the defvar table)
* Above also applies for certain proc calls
Just as a general warning:
I wouldn't input any DM code in here you don't want to get stolen, any input into someone else's server could have the potential to get logged.
In response to Flame Sage
Flame Sage wrote:
Just as a general warning:
I wouldn't input any DM code in here you don't want to get stolen, any input into someone else's server could have the potential to get logged.

That's an extremely infantile and redundant warning considering this is a legitimately useful resource, and also the likelihood this guy made a disassembler to steal yur speshul codez is just about nil.
Interesting:

proc/add(x, y)
. = x + y

/proc/add:
0: get arg<0>
3: get arg<1>
6: add
7: set defret
9: ret

proc/add(x, y)
return x + y

/proc/add:
0: get arg<0>
3: get arg<1>
6: add
7: popret
8: ret
proc/Add(a, b)
var/val = a + b
return val

/proc/Add:
0: get arg<0>
3: get arg<1>
6: add
7: set var<0>
10: get var<0>
13: popret
14: ret


Even more interesting stuff. Using the . operator is much faster than creating your own variable, and using the return statement directly is even slightly faster.
I'm gonna have to add some code that decodes the defvars structures and such, then I can nicely list the variable and argument names for each function

And then I need to make it store a proper indexable list of defvars without it becoming a mess

I should also restructure the whole way it handles opcodes to be much more flexible, which would make writing the IL recompiler simpler later on
In response to Doohl
Doohl wrote:
> proc/Add(a, b)
> var/val = a + b
> return val
>
> /proc/Add:
> 0: get arg<0>
> 3: get arg<1>
> 6: add
> 7: set var<0>
> 10: get var<0>
> 13: popret
> 14: ret
>

Even more interesting stuff. Using the . operator is much faster than creating your own variable, and using the return statement directly is even slightly faster.

Yep, so this means if you need to manipulate a single variable in a proc, you should always use ., and if you need to only return a value with no manipulation, use return.
or just do what you think makes the most readable code because an extra instruction isn't a big deal
In response to Giacomand
Giacomand wrote:
or just do what you think makes the most readable code because an extra instruction isn't a big deal

I would very much go for this. The tool provided is very interesting, and may (as the OP notes) be useful for understanding what is produced, which may help tweaking optimisation on heavy algorithms you cannot replace for whatever reason, but this stuff certainly ain't top priority for the majority of your codebase.
Most of my code problems are figuring out how to do something many many times in a single tick because the VM seems to be slow at calculating a lot of trig at once. So this is definitely helpful in those situations...

The majority of byond won't benefit from this because most people have very basic problems.
If you're using any math functions, note that:
/proc/a()
world << 5*5

/proc/b()
world << 5*abs(5)

/proc/a:
0: get world
2: push 25
4: out
5: ret
/proc/b:
0: get world
2: push 5
4: abs
5: push 5
7: mul
8: out
9: ret


It was mostly meant for some debugging purposes of my own, but it had some nice uses figuring out what is going on internally and generally an insight in how weird BYOND is in a lot of places
That kind of behaviour is normal for many optimising compilers. "5*5" is an easy expression to evaluate - to optimise out abs(5) requires proving some things about abs() (no side-effects, for example), and frankly that's a pain.

More complicated optimising compilers tend to do things like special-case simple mathematical functions applied to constants, but sometimes they get that wrong and weird things happen in edge cases.

Basically, not optimising out abs(5) is the correct decision for a project on DM's scale. Maybe if they were the visual studio compiler team it wouldn't be. But they don't have those resources and the potential benefit is minor.

Also:
That's an extremely infantile and redundant warning considering this is a legitimately useful resource, and also the likelihood this guy made a disassembler to steal yur speshul codez is just about nil.

Agreed.
abs(5) is actually pretty much no different than -5 operation wise with how the bytecode works right now

It actually does optimise that now because of some work Lummox JR did
In response to FIREking
FIREking wrote:
Most of my code problems are figuring out how to do something many many times in a single tick because the VM seems to be slow at calculating a lot of trig at once. So this is definitely helpful in those situations...

The majority of byond won't benefit from this because most people have very basic problems.

So very true.

99% of this community is not at the point where they would ever care about this or even understand when and why they should.
In response to MagicMountain
I think Stephen's ultimate point is that complex, does not equate into useful.

Everyone programs differently, despite being given the same tools.

Albert Einstein said it best to sum it all up--

"If you don't understand a subject enough to explain it simply, you don't understand it very well at all."


I personally believe that. It's nice to have a tool like this at your disposal-- but it's usefulness is really just dependent upon "how" you code your games.
atom/wtf/Del()
return

/atom/wtf/Del:
0: ret
1: ret

Double ret? Bug or i'm don't understand something?
In response to GerrCrazzy
Weird stuff

////////////////////////////////////
mob/Del()
return

/mob/Del:
0: ret
1: ret
////////////////////////////////////
mob/Del()
return ..()

/mob/Del:
0: pcall
1: popret
2: ret
////////////////////////////////////
mob/Del()
. = ..()

/mob/Del:
0: pcall
1: set defret
3: ret
////////////////////////////////////
mob/Del()
..()
return

/mob/Del:
0: pcall
1: pop
2: ret
3: ret
Double ret is because return compiles right down to ret/retval, theres also always a ret inserted by the compiler, so if you type return at the end you end up with two rets
O_o Dear god, I was right!

Thanks Tobba!

I've been writing my own VM for the last two days. I'm doing it with no experience in writing VMs, and I just started writing without doing any research. I'm actually pretty impressed with how close my solution is to Dan's, given that he produced that after being taught how to do VMs, and I produced something similar with pure inductive reasoning.

One thing that absolutely baffles me, is why they are using opcodes for built-in procs.

It would make more sense to me to store a C pointer to the function, then essentially post-compile, jam the internal function call pointers into a lookup table. Then you make an opcode for INSTRUCTION_INTERNAL_CALL and slap that bad boy in there.

That way, the opcode looks something like this:

push [arg1]..[argX] push [argcount] push [memaddr] icall


For any function overriding a built-in function, you just set up the proc index list to skip any values that have a memaddr pointer to an icall. That way, you can find out what ..() is simply by subtracting 1 from the reference index then accessing proc_refs[ref-1]
I'm not sure what I am looking at. What is the purpose/use of something like this?
Page: 1 2