[nolimips: 125] Li is not a native instruction

This patch reveals the lack of nolimips in immediate checking: li $t0, 0x10001 is invalid since 0x10001 does not fit in 16bits. In these cases, the assembler should output: lui $t0, 0x1 ori $t0, $t0, 0x10000 ... The worst is that this remark is valid for every immediate instructions (addi, ori, andi, etc.) Index: ChangeLog from Benoît Perrot <benoit@lrde.epita.fr> Li is not a native instruction * dev/nolimips.xml, src/int/program_builder.cc: Make `li' a pseudo instruction for `addiu'. * src/vm/cpu.hh, src/vm/cpu.cc: Remove emulation of `li'. Index: src/vm/cpu.hh --- src/vm/cpu.hh (revision 124) +++ src/vm/cpu.hh (revision 125) @@ -195,7 +195,6 @@ virtual void visit(const inst::Lb& lb); virtual void visit(const inst::Lbu& Lbu); virtual void visit(const inst::Lw& lw); - virtual void visit(const inst::Li& li); virtual void visit(const inst::Sb& sb); virtual void visit(const inst::Sw& sw); Index: src/vm/cpu.cc --- src/vm/cpu.cc (revision 124) +++ src/vm/cpu.cc (revision 125) @@ -365,12 +365,6 @@ // Move instructions // -------------------------------------------------------------------------- - void - Cpu::visit(const inst::Li& li) - { - set_register(li.get_dest (), li.get_imm ()); - } - // Store void Cpu::visit(const inst::Sb& sb) Index: src/inst/program_builder.cc --- src/inst/program_builder.cc (revision 124) +++ src/inst/program_builder.cc (revision 125) @@ -50,7 +50,8 @@ { // Warning: is not implemented in as for mips, check why. program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Mul(*dest, *src1, Register(Register::general, Cpu::at))); } @@ -67,7 +68,8 @@ { // FIXME: program_solver must warn and/or break when exp is zero program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Div(*src1, Register(Register::general, Cpu::at))); program_->text_section ().add_inst(new Mflo(*dest)); @@ -84,7 +86,8 @@ { // FIXME: program_solver must warn and/or break when exp is zero program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Divu(*src1, Register(Register::general, Cpu::at))); program_->text_section ().add_inst(new Mflo(*dest)); @@ -234,7 +237,8 @@ ProgramBuilder::add_sgt(Register *dest, Register *src1, Exp *src2) { program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Slt(*dest, Register(Register::general, Cpu::at), *src1)); } @@ -242,7 +246,8 @@ ProgramBuilder::add_sgtu(Register *dest, Register *src1, Exp *src2) { program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Sltu(*dest, Register(Register::general, Cpu::at), *src1)); } @@ -258,7 +263,8 @@ ProgramBuilder::add_sle(Register *dest, Register *src1, Exp *src2) { program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Slt(*dest, Register(Register::general, Cpu::at), *src1)); program_->text_section ().add_inst(new Xori(*dest, *dest, new IntExp(1))); @@ -273,7 +279,8 @@ ProgramBuilder::add_sleu(Register *dest, Register *src1, Exp *src2) { program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Sltu(*dest, Register(Register::general, Cpu::at), *src1)); program_->text_section (). @@ -288,7 +295,8 @@ ProgramBuilder::add_bne(Register *src1, Exp *src2, Exp *dest) { program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Bne(*src1, Register(Register::general, Cpu::at), dest)); // This nop is in the delay slot @@ -301,7 +309,8 @@ ProgramBuilder::add_beq(Register *src1, Exp *src2, Exp *dest) { program_->text_section (). - add_inst(new Li(Register(Register::general, Cpu::at), src2)); + add_inst(new Addiu(Register(Register::general, Cpu::at), + Register(Register::general, Cpu::zero), src2)); program_->text_section (). add_inst(new Beq(*src1, Register(Register::general, Cpu::at), dest)); // This nop is in the delay slot @@ -545,7 +554,10 @@ new IntExp(0))); } - // Load address ------------------------------------------ + // Load -------------------------------------------------- + + // Load address + // FIXME: alias? void ProgramBuilder::add_la (Register *dest, Exp *exp, Register *base) { @@ -554,8 +566,8 @@ void ProgramBuilder::add_la (Register *reg, Exp *exp) { - // FIXME: alias - program_->text_section ().add_inst(new Li(*reg, exp)); + program_->text_section (). + add_inst(new Addiu(*reg, Register(Register::general, Cpu::zero), exp)); } } // namespace inst Index: dev/nolimips.xml --- dev/nolimips.xml (revision 124) +++ dev/nolimips.xml (revision 125) @@ -1026,15 +1026,11 @@ </syntax> </instruction> - <!-- FIXME, should be a complex form --> - <instruction opcode="li" level="native" kind="load"> + <instruction opcode="li" level="pseudo" kind="load"> <description>Move the constant imm into dest.</description> - <format> - <attribute type="Register" name="dest"/> - <attribute type="Exp" name="imm"/> - </format> - <syntax> + <syntax alias="ori"> <token kind="register" /> + <token kind="hidden" value="new Register(Register::general, Cpu::zero)"/> <token kind="immediate" /> </syntax> </instruction>

Do not forget to launch `bootstrap', since an instruction was removed from nolimips.xml -- Noe

"Benoît" == Benoît Perrot <benoit@lrde.epita.fr> writes:
Do not forget to launch `bootstrap', since an instruction was removed from nolimips.xml
That's not good. The Makefiles should handle this. You should fix them rather than using bootstrap all the time. Have a look at src/ast/Makefile.am to see how tc handles this.

Akim Demaille <akim@epita.fr> writes:
Do not forget to launch `bootstrap', since an instruction was removed from nolimips.xml
That's not good. No, it is not :(
The Makefiles should handle this. You should fix them rather than using bootstrap all the time. Have a look at src/ast/Makefile.am to see how tc handles this.
From src/ast/Makefile.am:
include $(srcdir)/ast-nodes.mk [...] $(srcdir)/ast-nodes.mk: $(gen_dir)/ast-nodes-mk-gen $(ast_gen_deps) $(gen_dir)/ast-nodes-mk-gen < $(srcdir)/ast.xml >$@.tmp mv -f $@.tmp $@ ## ## I did not know that the generated Makefiles were so smart :P ## Nice! $(srcdir)/ast-fwd-gen.stamp: $(gen_dir)/ast-fwd-gen $(ast_gen_deps) @rm -rf $@.tmp @touch $@.tmp $(gen_dir)/ast-fwd-gen $(srcdir) < $(srcdir)/ast.xml @mv -f $@.tmp $@ fwd.hh: ast-fwd-gen.stamp ## ## Why use such a mechanism to update stamp? -- Noe

"Benoît" == Benoît Perrot <benoit@lrde.epita.fr> writes:
Akim Demaille <akim@epita.fr> writes:
Do not forget to launch `bootstrap', since an instruction was removed from nolimips.xml
That's not good. No, it is not :(
The Makefiles should handle this. You should fix them rather than using bootstrap all the time. Have a look at src/ast/Makefile.am to see how tc handles this.
From src/ast/Makefile.am:
include $(srcdir)/ast-nodes.mk
[...]
$(srcdir)/ast-nodes.mk: $(gen_dir)/ast-nodes-mk-gen $(ast_gen_deps) $(gen_dir)/ast-nodes-mk-gen < $(srcdir)/ast.xml >$@.tmp mv -f $@.tmp $@
## ## I did not know that the generated Makefiles were so smart :P ## Nice!
$(srcdir)/ast-fwd-gen.stamp: $(gen_dir)/ast-fwd-gen $(ast_gen_deps) @rm -rf $@.tmp @touch $@.tmp $(gen_dir)/ast-fwd-gen $(srcdir) < $(srcdir)/ast.xml @mv -f $@.tmp $@ fwd.hh: ast-fwd-gen.stamp
## ## Why use such a mechanism to update stamp?
Read Automake.texi. ---------------------------------------------------------------------- 26.6 Handling Tools that Produce Many Outputs ============================================= This section describes a `make' idiom that can be used when a tool produces multiple output files. It is not specific to Automake and can be used in ordinary `Makefile's. Suppose we have a program called `foo' that will read one file called `data.foo' and produce two files named `data.c' and `data.h'. We want to write a `Makefile' rule that captures this one-to-two dependency. The naive rule is incorrect: # This is incorrect. data.c data.h: data.foo foo data.foo What the above rule really says is that `data.c' and `data.h' each depend on `data.foo', and can each be built by running `foo data.foo'. In other words it is equivalent to: # We do not want this. data.c: data.foo foo data.foo data.h: data.foo foo data.foo which means that `foo' can be run twice. Usually it will not be run twice, because `make' implementations are smart enough to check for the existence of the second file after the first one has been built; they will therefore detect that it already exists. However there are a few situations where it can run twice anyway: * The most worrying case is when running a parallel `make'. If `data.c' and `data.h' are built in parallel, two `foo data.foo' commands will run concurrently. This is harmful. * Another case is when the dependency (here `data.foo') is (or depends upon) a phony target. A solution that works with parallel `make' but not with phony dependencies is the following: data.c data.h: data.foo foo data.foo data.h: data.c The above rules are equivalent to data.c: data.foo foo data.foo data.h: data.foo data.c foo data.foo therefore a parallel `make' will have to serialize the builds of `data.c' and `data.h', and will detect that the second is no longer needed once the first is over. Using this pattern is probably enough for most cases. However it does not scale easily to more output files (in this scheme all output files must be totally ordered by the dependency relation), so we will explore a more complicated solution. Another idea is to write the following: # There is still a problem with this one. data.c: data.foo foo data.foo data.h: data.c The idea is that `foo data.foo' is run only when `data.c' needs to be updated, but we further state that `data.h' depends upon `data.c'. That way, if `data.h' is required and `data.foo' is out of date, the dependency on `data.c' will trigger the build. This is almost perfect, but suppose we have built `data.h' and `data.c', and then we erase `data.h'. Then, running `make data.h' will not rebuild `data.h'. The above rules just state that `data.c' must be up-to-date with respect to `data.foo', and this is already the case. What we need is a rule that forces a rebuild when `data.h' is missing. Here it is: data.c: data.foo foo data.foo data.h: data.c @if test -f $@; then :; else \ rm -f data.c; \ $(MAKE) $(AM_MAKEFLAGS) data.c; \ fi The above scales easily to more outputs and more inputs. One of the output is picked up to serve as a witness of the run of the command, it depends upon all inputs, and all other outputs depend upon it. For instance if `foo' should additionally read `data.bar' and also produce `data.w' and `data.x', we would write: data.c: data.foo data.bar foo data.foo data.bar data.h data.w data.x: data.c @if test -f $@; then :; else \ rm -f data.c; \ $(MAKE) $(AM_MAKEFLAGS) data.c; \ fi There is still a minor problem with this setup. `foo' outputs four files, but we do not know in which order these files are created. Suppose that `data.h' is created before `data.c'. Then we have a weird situation. The next time `make' is run, `data.h' will appear older than `data.c', the second rule will be triggered, a shell will be started to execute the `if...fi' command, but actually it will just execute the `then' branch, that is: nothing. In other words, because the witness we selected is not the first file created by `foo', `make' will start a shell to do nothing each time it is run. A simple riposte is to fix the timestamps when this happens. data.c: data.foo data.bar foo data.foo data.bar data.h data.w data.x: data.c @if test -f $@; then \ touch $@; \ else \ rm -f data.c; \ $(MAKE) $(AM_MAKEFLAGS) data.c; \ fi Another solution, not incompatible with the previous one, is to use a different and dedicated file as witness, rather than using any of `foo''s outputs. data.stamp: data.foo data.bar @rm -f data.tmp @touch data.tmp foo data.foo data.bar @mv -f data.tmp $@ data.c data.h data.w data.x: data.stamp @if test -f $@; then \ touch $@; \ else \ rm -f data.stamp; \ $(MAKE) $(AM_MAKEFLAGS) data.stamp; \ fi `data.tmp' is created before `foo' is run, so it has a timestamp older than output files output by `foo'. It is then renamed to `data.stamp' after `foo' has run, because we do not want to update `data.stamp' if `foo' fails. Using a dedicated witness like this is very handy when the list of output files is not known beforehand. As an illustration, consider the following rules to compile many `*.el' files into `*.elc' files in a single command. It does not matter how `ELFILES' is defined (as long as it is not empty: empty targets are not accepted by POSIX). ELFILES = one.el two.el three.el ... ELCFILES = $(ELFILES:=c) elc-stamp: $(ELFILES) @rm -f elc-temp @touch elc-temp $(elisp_comp) $(ELFILES) @mv -f elc-temp $@ $(ELCFILES): elc-stamp @if test -f $@; then \ touch $@; \ else \ rm -f elc-stamp; \ $(MAKE) $(AM_MAKEFLAGS) elc-stamp; \ fi For completeness it should be noted that GNU `make' is able to express rules with multiple output files using pattern rules (*note Pattern Rule Examples: (make)Pattern Examples.). We do not discuss pattern rules here because they are not portable, but they can be convenient in packages that assume GNU `make'.

Akim Demaille <akim@epita.fr> writes:
$(srcdir)/ast-fwd-gen.stamp: $(gen_dir)/ast-fwd-gen $(ast_gen_deps) @rm -rf $@.tmp @touch $@.tmp $(gen_dir)/ast-fwd-gen $(srcdir) < $(srcdir)/ast.xml @mv -f $@.tmp $@ fwd.hh: ast-fwd-gen.stamp
## ## Why use such a mechanism to update stamp?
Read Automake.texi.
[...] Oh la :) You explained me, Then I used, The stamp trick When I was Working on TC's AST generation I meant: "why touch $@ is not enough?" Because: `data.tmp' is created before `foo' is run, so it has a timestamp older than output files output by `foo'. It is then renamed to `data.stamp' after `foo' has run, because we do not want to update `data.stamp' if `foo' fails. Thanks! -- Noe
participants (2)
-
Akim Demaille
-
Noe