switch the fatal error path to put_string
[nyanvsnprintf.git] / linux / arch / x64 / vsnprintf / finish_directives__numbered_argument_mode.S
blob17cba3e4053c106add594c942ca9118c75d80668
1 // XXX: we chose to factor *NOT* the numbered argument decoding with the auto-incremented argument
2 //      decoding.
3 #define LL(l) L(numbered_argument_mode__finish_directives__##l)
4         jmp LL(load_directive_state)
5         ASM_ALIGN_ZERO(CACHE_LINE_BYTES_N_LOG2)
6 LL(next_directive):
7         dec CONV_DIRECTIVES_N
8                 jz L(va_list_resolve)
9         sub ST_CONV_DIRECTIVE,QWORD_BYTES_N*CD_QWORDS_N // point on the next conversion direction
10 LL(load_directive_state):
11         mov CONV_DIRECTIVE_STATE,qword ptr [ST_CONV_DIRECTIVE+CD_STATE]
12         test CONV_DIRECTIVE_STATE,CD_STATE__PERCENT_DIRECTIVE // we skip percent directives
13                 jnz LL(next_directive)
14 // value argument number specification -- START ----------------------------------------------------
15 #define LLL(l) LL(arg_idx__##l)
16 #define VALUE_ARGUMENT_NUMBER rcx
17         // We are in numbered argument mode, all non immediat values are to be given an explicit
18         // argument number like the value to be actually converted. We prefer to scan for the
19         // terminal '$' of the value argument number instead of logically computing the end of this
20         // field (we do that for the following width and precision fields).
21         mov rdi,qword ptr [ST_CONV_DIRECTIVE+CD_START]
22         inc rdi // skip the '%' of the format
23         movzx rax,byte ptr [rdi]
24         movzx rax,byte ptr [rdi+1]
25         mov rsi,rdi // make a copy for later
26         mov rcx,qword ptr [ST_CONV_DIRECTIVE+CD_SPECIFIER] // our "end" for scanning the '$' char
27         sub rcx,rdi // max chars to scan
28                 jz L(put_string) // no room for even '$', bail out
29         mov al,0x24 // '$'
30         repne scasb // we did ensure we have at least 1 char to scan
31                 jne L(put_string) // failed to find the mandatory '$', bail out
32         // Here, rdi points past the found '$'
33         mov PREVIOUS_SPECIFICATION_END,rdi // keep that for the following flags decoder
34         dec rdi // make '$' the end pointer for the following decimal string conversion
35         xchg rsi,rdi // register dance to fix our registers for the string_decimal_conversion_nocheck inputs
36         lea LINK_REGISTER,[rel LLL(store)]
37         jmp L(string_decimal_conversion_nocheck) // clobber:rax,rdx,rcx(return value),rsi
38 LLL(store):
39         mov qword ptr [ST_CONV_DIRECTIVE+CD_VALUE_ARGUMENT_NUMBER],VALUE_ARGUMENT_NUMBER
40         // track the highest argument number
41         cmp VALUE_ARGUMENT_NUMBER,HIGHEST_ARGUMENT_NUMBER
42         cmova HIGHEST_ARGUMENT_NUMBER,VALUE_ARGUMENT_NUMBER // bigger one, do update
43 #undef VALUE_ARGUMENT_NUMBER
44 #undef LLL
45 // value argument number specification -- END ------------------------------------------------------
46 // flags specification -- START --------------------------------------------------------------------
47 #define LLL(l) LL(flags__##l)
48 #define FLAGS_CHARS_BITMAP      rdx
49 #define FLAG                    rsi
50 #define FLAG_CHAR               al
51         // In this mode, flags would always start at the end of the _value_ argument number.
52         mov FLAG,PREVIOUS_SPECIFICATION_END
53         mov FLAGS_CHARS_BITMAP,FLAGS_CHARS_BITMAP_VALUE
54         jmp LLL(load_next_format_char)
56 LLL(next_format_char):
57         inc FLAG
58 LLL(load_next_format_char):
59         mov FLAG_CHAR,byte ptr [FLAG]
60         test FLAG_CHAR,FLAG_CHAR
61                 jz L(put_string) // ERROR:unfinished conversion directive
62         mov cl,FLAG_CHAR
63         sub cl,0x20 // ' '
64         test cl,-64 // warp around and mod 64
65                 jnz LLL(epilog) // finished
66         bt FLAGS_CHARS_BITMAP,rcx
67                 jnc LLL(epilog) // finished
68         //------------------------------------------------------------------------------------------
69         // we have a flag to decode
70         //------------------------------------------------------------------------------------------
71 LLL(space_prefix):
72         cmp FLAG_CHAR,0x20 // ' '
73                 jne LLL(alternative_form)
74         or CONV_DIRECTIVE_STATE,CD_STATE__FLAGS__SPACE_PREFIX
75         jmp LLL(next_format_char)
76         //------------------------------------------------------------------------------------------
77 LLL(alternative_form):
78         cmp FLAG_CHAR,0x23 // '#'
79                 jne LLL(thousands_grouping)
80         or CONV_DIRECTIVE_STATE,CD_STATE__FLAGS__ALTERNATIVE_FORM
81         jmp LLL(next_format_char)
82         //------------------------------------------------------------------------------------------
83 LLL(thousands_grouping):
84         cmp FLAG_CHAR,0x27 // '''
85                 jne LLL(always_sign)
86         or CONV_DIRECTIVE_STATE,CD_STATE__FLAGS__THOUSANDS_GROUPING
87         jmp LLL(next_format_char)
88         //------------------------------------------------------------------------------------------
89 LLL(always_sign):
90         cmp FLAG_CHAR,0x2b // '+'
91                 jne LLL(left_justify)
92         or CONV_DIRECTIVE_STATE,CD_STATE__FLAGS__ALWAYS_SIGN
93         jmp LLL(next_format_char)
94         //------------------------------------------------------------------------------------------
95 LLL(left_justify):
96         cmp FLAG_CHAR,0x2d // '-'
97                 jne LLL(zero_padding)
98         or CONV_DIRECTIVE_STATE,CD_STATE__FLAGS__LEFT_JUSTIFY
99         jmp LLL(next_format_char)
100         //------------------------------------------------------------------------------------------
101 LLL(zero_padding):
102         cmp FLAG_CHAR,0x30 // '0'
103                 jne LLL(epilog)
104         or CONV_DIRECTIVE_STATE,CD_STATE__FLAGS__ZERO_PADDING
105         jmp LLL(next_format_char)
106         //------------------------------------------------------------------------------------------
107         ASM_ALIGN_ZERO(CACHE_LINE_BYTES_N_LOG2)
108 LLL(epilog):
109         test CONV_DIRECTIVE_STATE,CD_STATE__FLAGS_MSK
110                 jz LLL(no_update)
111         mov PREVIOUS_SPECIFICATION_END,FLAG
112         mov qword ptr [ST_CONV_DIRECTIVE+CD_STATE],CONV_DIRECTIVE_STATE // do a state update here
113         ASM_ALIGN_NOPS(CODE_FETCH_BYTES_N_LOG2)
114 LLL(no_update):
115 #undef FLAGS_CHARS_BITMAP
116 #undef FLAG
117 #undef FLAG_CHAR
118 #undef LLL
119 // width specification -- START --------------------------------------------------------------------
120 #define LLL(l) LL(width__##l)
121 #define WIDTH_START             rdi
122 #define WIDTH_END               rsi
123 #define WIDTH                   rcx
124         // Width specification, start and last format pointers:
125         // * Width specification start format pointer: if we have a flags specification, then it
126         //   will be the end pointer of this flags specification, if we don't have a flags
127         //   specification it will be the first char past the _value_ argument number specification,
128         //   namely past its '$' marker. Here this is the end of the previous specification.
129         // * Width specification end format pointer: if we have a precision specification, then it
130         //   will be the precision marker pointer, if no precision specification are here but we
131         //   have a length modifier, then it will be the the start of this length modifier (which
132         //   can be 2 chars), and the last case, if we don't even have a length modifier, it will be
133         //   the conversion specifier pointer itself.
134         // Of course, if start == end, there is no width specification.
135         //------------------------------------------------------------------------------------------
136         mov WIDTH_START,PREVIOUS_SPECIFICATION_END // we won't need the previous specification end for the precision decoder since we have the '.' marker
137         //------------------------------------------------------------------------------------------
138         mov WIDTH_END,qword ptr [ST_CONV_DIRECTIVE+CD_SPECIFIER]
139         test CONV_DIRECTIVE_STATE,CD_STATE__LENGTH_MODIFIER_MSK
140         cmovnz WIDTH_END,qword ptr [ST_CONV_DIRECTIVE+CD_LENGTH_MODIFIER_START]
141         test CONV_DIRECTIVE_STATE,CD_STATE__PRECISION_FOUND
142         cmovnz WIDTH_END,qword ptr [ST_CONV_DIRECTIVE+CD_PRECISION_MARKER]
143         cmp WIDTH_END,WIDTH_START // check the width is at least 1 char
144                 je LLL(epilog)
145         //------------------------------------------------------------------------------------------
146         // The width value may not be an immediat but stored into a function argument. It is
147         // decided with the '*' char.
148         cmp byte ptr [WIDTH_START],0x2a // '*'
149                 jne LLL(convert)
150         //------------------------------------------------------------------------------------------
151         // Since we are in numbered argument mode, we have to convert the string of the argument
152         // number. In other words, the width is not an immediat value but an argument number.
153         // The width should have a '$' as the last char (but we don't check for it, we just skip
154         // it).
155         inc WIDTH_START // skip the '*'
156         dec WIDTH_END // skip the '$' but we do not check for it
158         // update the conversion directive state in the stack
159         or CONV_DIRECTIVE_STATE,CD_STATE__WIDTH_IS_ARGUMENT_NUMBER
160         mov qword ptr [ST_CONV_DIRECTIVE+CD_STATE],CONV_DIRECTIVE_STATE
162         // We did check only for a width of 1 char which is not enough
163         cmp WIDTH_END,WIDTH_START
164                 jbe L(put_string) // invalid width argument number, bail out
165         //------------------------------------------------------------------------------------------
166 LLL(convert):
167         // nocheck decimal string conversion, inputs are properly setup
168         lea LINK_REGISTER,[rel LLL(store)] // using the link register for the return address
169         jmp L(string_decimal_conversion_nocheck) // clobber:rax,rdx,rcx(return value),rsi
170 LLL(store):
171         mov qword ptr [ST_CONV_DIRECTIVE+CD_WIDTH],WIDTH // output of the string decimal conversion code path
172         test CONV_DIRECTIVE_STATE,CD_STATE__WIDTH_IS_ARGUMENT_NUMBER
173                 jz LLL(epilog) // width is a immediat value, no highest argument number to update
174         cmp WIDTH,HIGHEST_ARGUMENT_NUMBER // here, width does actually contain the argument number
175         cmova HIGHEST_ARGUMENT_NUMBER,WIDTH
176         ASM_ALIGN_NOPS(CACHE_LINE_BYTES_N_LOG2)
177 LLL(epilog):
178 #undef WIDTH_START
179 #undef WIDTH_END
180 #undef WIDTH
181 #undef LLL
182 // width specification -- END ----------------------------------------------------------------------
183 // precision specification -- START ----------------------------------------------------------------
184 #define LLL(l) LL(precision__##l)
185 #define PRECISION_START         rdi
186 #define PRECISION_END           rsi
187 #define PRECISION               rcx
188         test CONV_DIRECTIVE_STATE,CD_STATE__PRECISION_FOUND
189                 jz LL(next_directive) // the precision was inited at 0 once a '.' marker was found
190         // Precision specification, start and last format pointers:
191         // * Precision specification start format pointer: the char following the precision marker.
192         // * Precision specification end format pointer: if we have length modifier it will be its
193         //   first char, but if we don't have a length modifier, it will be the pointer on the 
194         //   specifier itself.
195         // Of course, if start == end, there is a precision of 0.
196         //------------------------------------------------------------------------------------------
197         mov PRECISION_START,qword ptr [ST_CONV_DIRECTIVE+CD_PRECISION_MARKER]
198         inc PRECISION_START // skip '.'
199         mov PRECISION_END,qword ptr [ST_CONV_DIRECTIVE+CD_SPECIFIER]
200         test CONV_DIRECTIVE_STATE,CD_STATE__LENGTH_MODIFIER_MSK
201         cmovnz PRECISION_END,qword ptr [ST_CONV_DIRECTIVE+CD_LENGTH_MODIFIER_START]
202         cmp PRECISION_END,PRECISION_START // at least 1 char?
203                 je LL(next_directive) // precision was initide to 0 once the '.' marker was found
204         //------------------------------------------------------------------------------------------
205         // The precision value may not be an immediat but stored into a function argument. It is
206         // decided with the '*' char. Since we are in the numbered argument mode, do convert
207         // the string decimal value to an argument number.
208         cmp byte ptr [PRECISION_START],0x2a // '*'
209                 jne LLL(convert)
210         //------------------------------------------------------------------------------------------
211         // Since we are in numbered argument mode, we have to convert the string of the argument
212         // number. In other words, the precision is not an immediat value but an argument number.
213         // The precision should have a '$' as the last char (but we don't check for it, we just skip
214         // it).
215         inc PRECISION_START // skip the '*'
216         dec PRECISION_END // skip the '$' which we are not checking for
218         or CONV_DIRECTIVE_STATE,CD_STATE__PRECISION_IS_ARGUMENT_NUMBER
219         mov qword ptr [ST_CONV_DIRECTIVE+CD_STATE],CONV_DIRECTIVE_STATE
221         // We did check only for a width of 1 char which is not enough
222         cmp PRECISION_END,PRECISION_START
223                 jbe L(put_string) // invalid precision argument number (this is _not_ the same as being empty), bail out
224         //------------------------------------------------------------------------------------------
225 LLL(convert):
226         // nocheck decimal string conversion, inputs are properly setup
227         lea LINK_REGISTER,[rel LLL(store)] // using the link register for the return address
228         jmp L(string_decimal_conversion_nocheck) // clobber:rax,rdx,rcx(return value),rsi
229 LLL(store):
230         mov qword ptr [ST_CONV_DIRECTIVE+CD_PRECISION],PRECISION
231         test CONV_DIRECTIVE_STATE,CD_STATE__PRECISION_IS_ARGUMENT_NUMBER
232                 jz LL(next_directive) // precision is a immediat value, no highest argument number to update
233         cmp PRECISION,HIGHEST_ARGUMENT_NUMBER // here, precision does actually contain the argument number
234         cmova HIGHEST_ARGUMENT_NUMBER,PRECISION
235         jmp LL(next_directive)
236 #undef LLL // precision
237 // precision specification -- END-------------------------------------------------------------------
238 #undef LL // numbered_argument_mode__finish_directives