@@ -2862,6 +2862,9 @@ struct AsmProcessor {
2862
2862
}
2863
2863
operand = &operands[i];
2864
2864
2865
+ // This is used only in the case of `operand->cls` being `Opr_Mem`.
2866
+ bool hasConstDisplacement = false ;
2867
+
2865
2868
switch (operand->cls ) {
2866
2869
case Opr_Immediate:
2867
2870
// for implementing offset:
@@ -2959,9 +2962,13 @@ struct AsmProcessor {
2959
2962
" branching instruction" );
2960
2963
}
2961
2964
}
2962
- if ((operand->segmentPrefix != Reg_Invalid &&
2963
- operand->symbolDisplacement .length == 0 ) ||
2964
- operand->constDisplacement ) {
2965
+
2966
+ // We make note of this for later on, as if a const-displacement is in play we
2967
+ // may need to change the asm template to avoid it expanding to an invalid syntax.
2968
+ hasConstDisplacement = (operand->segmentPrefix != Reg_Invalid &&
2969
+ operand->symbolDisplacement .length == 0 ) ||
2970
+ operand->constDisplacement ;
2971
+ if (hasConstDisplacement) {
2965
2972
insnTemplate << operand->constDisplacement ;
2966
2973
if (operand->symbolDisplacement .length ) {
2967
2974
insnTemplate << ' +' ;
@@ -3057,6 +3064,29 @@ struct AsmProcessor {
3057
3064
// addOperand2("${", ":c}", Arg_Pointer, e,
3058
3065
// asmcode);
3059
3066
} else {
3067
+ // We don't know ahead of time what kind of memory operand will be generated for
3068
+ // our template: it could be a symbolic reference, or a Scale-Index-Base with or without
3069
+ // an offset. So, if we have a const-displacement of 4, it could look like one of:
3070
+ // `4+someGlobalVariable`, or `4+8(%ebp)`, or `4+(%ebp)`.
3071
+ // Notice how that last possibility is an invalid syntax, it should be `4(%ebp)`.
3072
+ // But, if we removed the `+`, then the other possibilities would become invalid or wrong.
3073
+ // We don't know which form will be generated, but we can force the third possibility to
3074
+ // be like the second possibility.
3075
+ //
3076
+ // For x86 assembly templates, memory operands can be marked with the `H` modifier, which
3077
+ // unconditionally adds an offset of 8 to the memory operand.
3078
+ // If we subtract 8 from our const-displacement, then we can use the `H` modifier to ensure
3079
+ // that we always end up with a valid syntax for a memory operand with an offset.
3080
+ // So, we do just that when we have const-displacement in play.
3081
+ // (Only for non-naked asm, as this isn't an issue for naked asm.)
3082
+ //
3083
+ // See also: https://lists.llvm.org/pipermail/llvm-dev/2017-August/116244.html
3084
+ const auto forceLeadingDisplacement = hasConstDisplacement && !sc->func ->isNaked ();
3085
+ if (forceLeadingDisplacement) {
3086
+ // Subtract 8 from our const-displacement, and prepare to add the 8 from the `H` modifier.
3087
+ insnTemplate << " -8+" ;
3088
+ }
3089
+
3060
3090
if (use_star) {
3061
3091
insnTemplate << ' *' ;
3062
3092
use_star = false ;
@@ -3069,7 +3099,14 @@ struct AsmProcessor {
3069
3099
e->type = tt;
3070
3100
}
3071
3101
3072
- addOperand (fmt, Arg_Memory, e, asmcode, mode);
3102
+ if (forceLeadingDisplacement) {
3103
+ // We have a const-displacement in play, so we add the `H` modifier, as described earlier.
3104
+ insnTemplate << " ${" << " <<" << (mode == Mode_Input ? " in" : " out" )
3105
+ << asmcode->args .size () << " >>" << " :H}" ;
3106
+ asmcode->args .push_back (AsmArg (Arg_Memory, e, mode));
3107
+ } else {
3108
+ addOperand (fmt, Arg_Memory, e, asmcode, mode);
3109
+ }
3073
3110
}
3074
3111
}
3075
3112
}
0 commit comments