Bug 131612

Summary: JavaScriptCore: ARM build fix after r167094.
Product: WebKit Reporter: László Langó <llango.u-szeged>
Component: JavaScriptCoreAssignee: László Langó <llango.u-szeged>
Status: RESOLVED FIXED    
Severity: Normal CC: cand, dbatyai.u-szeged, mark.lam, msaboff, ossy
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 108645, 131205    
Attachments:
Description Flags
Patch
none
Patch
none
Patch none

Description László Langó 2014-04-14 03:59:37 PDT
After r167094 there are many build errors on ARM like these:

    /tmp/ccgtHRno.s:370: Error: invalid constant (425a) after fixup
    /tmp/ccgtHRno.s:374: Error: invalid constant (426e) after fixup
    /tmp/ccgtHRno.s:378: Error: invalid constant (4282) after fixup
    /tmp/ccgtHRno.s:382: Error: invalid constant (4296) after fixup

Problem is caused by the wrong generated assembly like:
    "\tmov r2, (" LOCAL_LABEL_STRING(llint_op_strcat) " - " LOCAL_LABEL_STRING(relativePCBase) ")\n" // /home/webkit/WebKit/Source/JavaScriptCore/llint/LowLevelInterpreter.asm:741

`mov` can only move 8bit immediate, but not every constant fit into 8 bit. We should use `mow` rather than. CLang changes `mov` to `mow` automatically, but binutils doesn't.
Comment 1 László Langó 2014-04-14 04:11:51 PDT
Created attachment 229278 [details]
Patch
Comment 2 Peter Gal 2014-04-14 07:49:04 PDT
Comment on attachment 229278 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=229278&action=review

> Source/JavaScriptCore/ChangeLog:18
> +        `mov` can only move 8 bit immediate, but not every constant fit into 8 bit. We should use `movw` rather than.

The last sentence is a bit awkward for me.

> Source/JavaScriptCore/ChangeLog:19
> +        CLang changes `mov` to `movw` automatically, but binutils doesn't.

This doesn't mention the movt, which is also important. AFAIK clang converts the mov to a single movw or a movw and a movt, depending on the immediate.

> Source/JavaScriptCore/offlineasm/arm.rb:75
> +      @mov_counter ||= 0
> +      @mov_counter += 1
> +      const_label = "_mov_constant_label#{@mov_counter}"

Hmm... if we use @-s then it'll be an instance variable, and so we can end up with the same 'const_label' values. Maybe we can use $ so it'll be a global variable.
Or we could skip the whole counter, because it is valid to overwrite an assembly symbol with another .equ, but that approach could make the reading/understanding of the code bit harder.
Comment 3 László Langó 2014-04-14 07:51:09 PDT
Created attachment 229283 [details]
Patch
Comment 4 Darin Adler 2014-04-14 08:35:05 PDT
Comment on attachment 229283 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=229283&action=review

> Source/JavaScriptCore/offlineasm/arm.rb:78
> -      $asm.puts "mov #{register.armOperand}, (#{value})"
> +      $mov_counter ||= 0
> +      $mov_counter += 1
> +      const_label = "_mov_constant_label#{$mov_counter}"
> +      $asm.puts ".equ #{const_label}, (#{value})"
> +      $asm.puts "movw #{register.armOperand}, \#:lower16:#{const_label}"
> +      $asm.puts "movt #{register.armOperand}, \#:upper16:#{const_label}"

Won’t this hurt performance for constants that do fit into 8 bits, under clang at least?
Comment 5 Csaba Osztrogonác 2014-04-14 10:03:20 PDT
Comment on attachment 229283 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=229283&action=review

>> Source/JavaScriptCore/offlineasm/arm.rb:78
>> +      $asm.puts "movw #{register.armOperand}, \#:lower16:#{const_label}"
>> +      $asm.puts "movt #{register.armOperand}, \#:upper16:#{const_label}"
> 
> Won’t this hurt performance for constants that do fit into 8 bits, under clang at least?

I checked the generated assembly after the proposed fix, and it seems all offsets 
fit in 16 bits, but none of them fit in 8 bits, so we can simple use movw always:

$asm.puts "movw #{register.armOperand}, #{const_label}"

Or we could ... if GNU assembler supported it ... but I got build error:
Error: constant expression expected -- `movw r2,_mov_constant_label118'

It's so strange ...
$asm.puts "movw #{register.armOperand}, \#:lower16:#{const_label}" works fine, but doesn't work without :lower16:
Comment 6 Csaba Osztrogonác 2014-04-14 10:17:04 PDT
We can use this oneliner fix if we use :lower16:
-      $asm.puts "mov #{register.armOperand}, (#{value})"
+      $asm.puts "movw #{register.armOperand}, \#:lower16:(#{value})"

But it might cause strange failures in the future once 
the size of the constant becomes bigger than 16 bits.
Comment 7 Csaba Osztrogonác 2014-04-14 11:03:48 PDT
Or we can use this to make sure we won't loose any information in the future:
-      $asm.puts "mov #{register.armOperand}, (#{value})"
+      $asm.puts "movw #{register.armOperand}, \#:lower16:(#{value})"
+      $asm.puts "movt #{register.armOperand}, \#:upper16:(#{value})"

I think this extra assembly instruction won't have significant
performance effect, because llint_entry runs only once to fill
the entry point table. Am I right? Michael? Mark?
Comment 8 Michael Saboff 2014-04-14 20:25:45 PDT
(In reply to comment #7)
> Or we can use this to make sure we won't loose any information in the future:
> -      $asm.puts "mov #{register.armOperand}, (#{value})"
> +      $asm.puts "movw #{register.armOperand}, \#:lower16:(#{value})"
> +      $asm.puts "movt #{register.armOperand}, \#:upper16:(#{value})"
> 
> I think this extra assembly instruction won't have significant
> performance effect, because llint_entry runs only once to fill
> the entry point table. Am I right? Michael? Mark?

When I created the original patch, my research showed that mov is a pseudo op that the assembler would convert as needed to a single 8 or 16 bit immediate move or a movw/movt pair.  Certainly that is what we see with clang.  Gas/gcc should do the same.  It is also apparent that we need 16 bits (currently 15 bits) for llint_entry.

It is the case that llint_entry is intended to be run through this path only during startup.  However I don't want to change all arm immediate moves to be 16 bit though, as many, possible most of the llint immediate moves have values that fit in 8 bits.

Another way of handling this is to make the use of a 16 bit move predicated on an add or subtract of two labels and keep the current path for other cases.
Comment 9 Michael Saboff 2014-04-14 20:28:29 PDT
Comment on attachment 229283 [details]
Patch

As explained in https://bugs.webkit.org/show_bug.cgi?id=131612#c8
Comment 10 Csaba Osztrogonác 2014-04-16 04:32:29 PDT
(In reply to comment #8)
> (In reply to comment #7)
> When I created the original patch, my research showed that mov is a pseudo op that the assembler would convert as needed to a single 8 or 16 bit immediate move or a movw/movt pair.  Certainly that is what we see with clang.  Gas/gcc should do the same.  It is also apparent that we need 16 bits (currently 15 bits) for llint_entry.

gas can only convert mov rn, (label1 - label2) to T1 (imm8) 
and T2 (imm12) and can't convert to t3 (imm16). 

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html - A8.8.102 MOV (immediate)

Using movw and movt here won't affect other 8/16 bit immediate moves,
because string argument is only used for llint_entry now, nowhere else.
8/12/16 bits immediate values are handled after the string case:
http://trac.webkit.org/browser/trunk/Source/JavaScriptCore/offlineasm/arm.rb#L70

> It is the case that llint_entry is intended to be run through this path only during startup.  However I don't want to change all arm immediate moves to be 16 bit though, as many, possible most of the llint immediate moves have values that fit in 8 bits.
> 
> Another way of handling this is to make the use of a 16 bit move predicated on an add or subtract of two labels and keep the current path for other cases.

I think we can add a new ARM specific offline assembler instruction
only for this llint_entry use case: move rn, (label1-label2) which
translated to movw and movt.
Comment 11 László Langó 2014-04-16 07:07:19 PDT
Created attachment 229442 [details]
Patch
Comment 12 Michael Saboff 2014-04-16 19:24:06 PDT
Comment on attachment 229442 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=229442&action=review

> Source/JavaScriptCore/llint/LowLevelInterpreter.asm:745
> +    elsif ARM or ARMv7 or ARMv7_TRADITIONAL
> +        mvlbl (label - _relativePCBase), t2
> +        addp t2, t1, t2
> +        move index, t3
> +        storep t2, [a0, t3, 4]
> +    elsif SH4

If the issue only manifests with ARM traditional (not Thumb2), could you make this "elsif ARM or ARMv7_TRADITIONAL"?

> Source/JavaScriptCore/offlineasm/arm.rb:489
> +        when "mvlbl"
> +                $asm.puts "movw #{operands[1].armOperand}, \#:lower16:#{operands[0].value}"
> +                $asm.puts "movt #{operands[1].armOperand}, \#:upper16:#{operands[0].value}"

I believe that the movw is sufficient.
Comment 13 Csaba Osztrogonác 2014-04-20 06:52:15 PDT
(In reply to comment #12)
> (From update of attachment 229442 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=229442&action=review
> 
> > Source/JavaScriptCore/llint/LowLevelInterpreter.asm:745
> > +    elsif ARM or ARMv7 or ARMv7_TRADITIONAL
> > +        mvlbl (label - _relativePCBase), t2
> > +        addp t2, t1, t2
> > +        move index, t3
> > +        storep t2, [a0, t3, 4]
> > +    elsif SH4
> 
> If the issue only manifests with ARM traditional (not Thumb2), could you make this "elsif ARM or ARMv7_TRADITIONAL"?

No, it isn't ARM traditional issue. The bug is valid on 
ARM traditional and ARM Thumb2 instruction sets too. 

> > Source/JavaScriptCore/offlineasm/arm.rb:489
> > +        when "mvlbl"
> > +                $asm.puts "movw #{operands[1].armOperand}, \#:lower16:#{operands[0].value}"
> > +                $asm.puts "movt #{operands[1].armOperand}, \#:upper16:#{operands[0].value}"
> 
> I believe that the movw is sufficient.

It is sufficient now. But what if once the difference of offsets 
become bigger than 16 bit? It would cause obviously wrong behaviour.
Unfortunately there isn't a good way to determine if this immediate
fits to 16 bit or not in build time.
Comment 14 Csaba Osztrogonác 2014-04-20 06:58:59 PDT
Comment on attachment 229442 [details]
Patch

Clearing flags on attachment: 229442

Committed r167566: <http://trac.webkit.org/changeset/167566>
Comment 15 Csaba Osztrogonác 2014-04-20 06:59:06 PDT
All reviewed patches have been landed.  Closing bug.
Comment 16 Filip Pizlo 2014-04-21 20:16:34 PDT
Comment on attachment 229442 [details]
Patch

You shouldn't be adding new instructions for this.  It's straightforward to modify the lowering of "move" to realize that on ARM we need to emit the movw/movt pair in case of label differences.  Second, if you're going to add an instruction for moving labels (er, label differences) into registers, then you should change setEntryAddress to use this instruction on all architectures and not make it ARM-specific.  This would allow you to remove some redundant code.

In the future, please put some thought into how to make your changes minimal.  This is important for avoiding bugs in the future.
Comment 17 cand 2014-12-17 01:15:43 PST
This patch broke the ARMv6 build.

movw and movt are not available on v6, and other places use ldr instead, with a check for v7.

https://github.com/clbr/webkitfltk/issues/7
Comment 18 Csaba Osztrogonác 2015-02-05 05:15:20 PST
(In reply to comment #17)
> This patch broke the ARMv6 build.
> 
> movw and movt are not available on v6, and other places use ldr instead,
> with a check for v7.
> 
> https://github.com/clbr/webkitfltk/issues/7

new bug report for it: bug141288