Open nomennescio opened 6 years ago
Yeah, I didn't add it because I wasn't sure what to support (NASM or C) and wanted to keep it simple at first. Codewars UI should hide the preloaded editor though.
In my opinion, preloaded should contain NASM code because you usually hide a copy of your solution in there.
For random tests? For NASM, the reference solution can be written in C for that purpose.
Yes for random tests. If you use NASM in Preloaded, you can just reuse your own solution for it. If you want, you can still code another solution in C under the 'Test Cases'.
Having Preloaded in the same language as the solution language is as far as I can see standard practice for all languages.
There's little point to make it in C because there's already a module in C, the tests. The question is whether the code in Preloaded should be assembled separately or concatenated with user code. In the latter case it could contain some compile-time things like macros.
@DonaldKellett Assuming that this applies to NASM too:
If the argument is that all C-related boilerplate for assertions can be simply placed directly in the sample / submission tests, I mentioned above that it could be useful to place them once in Preloaded instead so they can be reused by both sample and submission tests, instead of copy-pasting the same thing twice between sample and submission tests.
Apart from the issue of non-self-sufficient sample tests... At least there already is a place for testing code in C, but what about testing code in asm? For example, what about clearing registers before calling a solution?
extern int chain (int init_val, size_t length, const funcptr functions[length]);
static int chain_proxy(int init_val, size_t length, const funcptr functions[length]) {
int result;
__asm(
"mov $111111111,%%eax;"
"mov $111111111,%%ecx; mov $111111111,%%r8; mov $111111111,%%r9;"
"mov $111111111,%%ebx; mov $111111111,%%r10; mov $111111111,%%r11;"
"mov $111111111,%%r12; mov $111111111,%%r13; mov $111111111,%%r14; mov $111111111,%%r15;"
"call chain"
: "=a"(result),
"+D"(init_val),
"+S"(length),
"+d"(functions)
:
: "rcx", "r8", "r9", "rbx", "r10", "r11", "r12", "r13", "r14", "r15", "cc", "memory"
);
return result;
}
Should something like this be in NASM or in inline asm in C? @monadius
For example, what about clearing registers before calling a solution?
That does sound a bit more tedious in C compared to plain assembly, but I doubt many Kata authors / translators will bother doing so. Though I understand why some Kata authors may want to clear registers before each call, for the sake of reproducible behavior.
That said, I personally disagree with clearing registers before calling the solution. Mostly compilers surely do not generate assembly that clears out registers before each procedure call, at least by default. It's a natural consequence of program execution that registers whose values aren't explicitly set right before a procedure call may contain garbage values arising from prior computation. Solvers who decide to tackle Kata in assembly should fully expect that and engineer their (hopefully) hand-tuned assembly solutions to properly follow architecture, compiler and operating system calling conventions. They should also accept the fact that there's a lot more room for undefined behavior due to bugs in assembly compared to C, and account for that when developing their solution.
The thing is that registers often contain not random values, but something relevant in the context, for example, copies of input. I made that fork after my solution that used the wrong register passed the tests and I didn't even notice that until later. There's no reliable way to pass 32-bit inputs with nonzero upper bits in pure C either. 32-bit inputs are almost always zero-extended to 64 bits (at least because that's what instructions normally do with 32-bit operands), but it isn't required by the ABI, so this fact is used in rare cases. For example,
int bar(int x);
int foo(long x) {
return bar(x);
}
is compiled into a single jump. And if bar
incorrectly uses all 64 bits of its input where they affect the result, it will work in most cases, but not in this case.
I prefer to write code for clearing registers and other special tests in NASM. It is much cleaner in NASM rather than in inline assembly. One thing which I failed to find about inline assembly is how to make sure that the stack is aligned properly before making a function call. The only solution I found is to align the stack manually with something like sub rsp, 16
and rsp, ~15
which is quite tedious and potentially easy to forget.
There are a lot of NASM solutions (especially in simple kata) where solvers do not follow the ABI64 and the code is not properly tested. Main issues are: modifying rbx
, rbp
, etc; not clearing higher 32 bits of inputs when inputs are in edi
, esi
, etc. And there is an issue with expected
result being in one of registers (and there is no reliable way to control it without inline assembly). So I prefer to have preloaded code in NASM because it is much easier to write special tests directly in NASM.
When trying to create a NASM translation, I noticed that none of the code in the Preloaded section is used by the Runner. Even if I type in complete rubbish, it is totally ignored, no compile or link error whatsoever.