1// Copyright 2014 Google Inc. All Rights Reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the COPYING file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
8// -----------------------------------------------------------------------------
9//
10// MIPS version of lossless functions
11//
12// Author(s):  Djordje Pesut    (djordje.pesut@imgtec.com)
13//             Jovan Zelincevic (jovan.zelincevic@imgtec.com)
14
15#include "./dsp.h"
16#include "./lossless.h"
17
18#if defined(WEBP_USE_MIPS32)
19
20#include <assert.h>
21#include <math.h>
22#include <stdlib.h>
23#include <string.h>
24
25#define APPROX_LOG_WITH_CORRECTION_MAX  65536
26#define APPROX_LOG_MAX                   4096
27#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086
28
29static float FastSLog2Slow(uint32_t v) {
30  assert(v >= LOG_LOOKUP_IDX_MAX);
31  if (v < APPROX_LOG_WITH_CORRECTION_MAX) {
32    uint32_t log_cnt, y, correction;
33    const int c24 = 24;
34    const float v_f = (float)v;
35    uint32_t temp;
36
37    // Xf = 256 = 2^8
38    // log_cnt is index of leading one in upper 24 bits
39    __asm__ volatile(
40      "clz      %[log_cnt], %[v]                      \n\t"
41      "addiu    %[y],       $zero,        1           \n\t"
42      "subu     %[log_cnt], %[c24],       %[log_cnt]  \n\t"
43      "sllv     %[y],       %[y],         %[log_cnt]  \n\t"
44      "srlv     %[temp],    %[v],         %[log_cnt]  \n\t"
45      : [log_cnt]"=&r"(log_cnt), [y]"=&r"(y),
46        [temp]"=r"(temp)
47      : [c24]"r"(c24), [v]"r"(v)
48    );
49
50    // vf = (2^log_cnt) * Xf; where y = 2^log_cnt and Xf < 256
51    // Xf = floor(Xf) * (1 + (v % y) / v)
52    // log2(Xf) = log2(floor(Xf)) + log2(1 + (v % y) / v)
53    // The correction factor: log(1 + d) ~ d; for very small d values, so
54    // log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v
55    // LOG_2_RECIPROCAL ~ 23/16
56
57    // (v % y) = (v % 2^log_cnt) = v & (2^log_cnt - 1)
58    correction = (23 * (v & (y - 1))) >> 4;
59    return v_f * (kLog2Table[temp] + log_cnt) + correction;
60  } else {
61    return (float)(LOG_2_RECIPROCAL * v * log((double)v));
62  }
63}
64
65static float FastLog2Slow(uint32_t v) {
66  assert(v >= LOG_LOOKUP_IDX_MAX);
67  if (v < APPROX_LOG_WITH_CORRECTION_MAX) {
68    uint32_t log_cnt, y;
69    const int c24 = 24;
70    double log_2;
71    uint32_t temp;
72
73    __asm__ volatile(
74      "clz      %[log_cnt], %[v]                      \n\t"
75      "addiu    %[y],       $zero,        1           \n\t"
76      "subu     %[log_cnt], %[c24],       %[log_cnt]  \n\t"
77      "sllv     %[y],       %[y],         %[log_cnt]  \n\t"
78      "srlv     %[temp],    %[v],         %[log_cnt]  \n\t"
79      : [log_cnt]"=&r"(log_cnt), [y]"=&r"(y),
80        [temp]"=r"(temp)
81      : [c24]"r"(c24), [v]"r"(v)
82    );
83
84    log_2 = kLog2Table[temp] + log_cnt;
85    if (v >= APPROX_LOG_MAX) {
86      // Since the division is still expensive, add this correction factor only
87      // for large values of 'v'.
88
89      const uint32_t correction = (23 * (v & (y - 1))) >> 4;
90      log_2 += (double)correction / v;
91    }
92    return (float)log_2;
93  } else {
94    return (float)(LOG_2_RECIPROCAL * log((double)v));
95  }
96}
97
98// C version of this function:
99//   int i = 0;
100//   int64_t cost = 0;
101//   const uint32_t* pop = &population[4];
102//   const uint32_t* LoopEnd = &population[length];
103//   while (pop != LoopEnd) {
104//     ++i;
105//     cost += i * *pop;
106//     cost += i * *(pop + 1);
107//     pop += 2;
108//   }
109//   return (double)cost;
110static double ExtraCost(const uint32_t* const population, int length) {
111  int i, temp0, temp1;
112  const uint32_t* pop = &population[4];
113  const uint32_t* const LoopEnd = &population[length];
114
115  __asm__ volatile(
116    "mult   $zero,    $zero                  \n\t"
117    "xor    %[i],     %[i],       %[i]       \n\t"
118    "beq    %[pop],   %[LoopEnd], 2f         \n\t"
119  "1:                                        \n\t"
120    "lw     %[temp0], 0(%[pop])              \n\t"
121    "lw     %[temp1], 4(%[pop])              \n\t"
122    "addiu  %[i],     %[i],       1          \n\t"
123    "addiu  %[pop],   %[pop],     8          \n\t"
124    "madd   %[i],     %[temp0]               \n\t"
125    "madd   %[i],     %[temp1]               \n\t"
126    "bne    %[pop],   %[LoopEnd], 1b         \n\t"
127  "2:                                        \n\t"
128    "mfhi   %[temp0]                         \n\t"
129    "mflo   %[temp1]                         \n\t"
130    : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
131      [i]"=&r"(i), [pop]"+r"(pop)
132    : [LoopEnd]"r"(LoopEnd)
133    : "memory", "hi", "lo"
134  );
135
136  return (double)((int64_t)temp0 << 32 | temp1);
137}
138
139// C version of this function:
140//   int i = 0;
141//   int64_t cost = 0;
142//   const uint32_t* pX = &X[4];
143//   const uint32_t* pY = &Y[4];
144//   const uint32_t* LoopEnd = &X[length];
145//   while (pX != LoopEnd) {
146//     const uint32_t xy0 = *pX + *pY;
147//     const uint32_t xy1 = *(pX + 1) + *(pY + 1);
148//     ++i;
149//     cost += i * xy0;
150//     cost += i * xy1;
151//     pX += 2;
152//     pY += 2;
153//   }
154//   return (double)cost;
155static double ExtraCostCombined(const uint32_t* const X,
156                                const uint32_t* const Y, int length) {
157  int i, temp0, temp1, temp2, temp3;
158  const uint32_t* pX = &X[4];
159  const uint32_t* pY = &Y[4];
160  const uint32_t* const LoopEnd = &X[length];
161
162  __asm__ volatile(
163    "mult   $zero,    $zero                  \n\t"
164    "xor    %[i],     %[i],       %[i]       \n\t"
165    "beq    %[pX],    %[LoopEnd], 2f         \n\t"
166  "1:                                        \n\t"
167    "lw     %[temp0], 0(%[pX])               \n\t"
168    "lw     %[temp1], 0(%[pY])               \n\t"
169    "lw     %[temp2], 4(%[pX])               \n\t"
170    "lw     %[temp3], 4(%[pY])               \n\t"
171    "addiu  %[i],     %[i],       1          \n\t"
172    "addu   %[temp0], %[temp0],   %[temp1]   \n\t"
173    "addu   %[temp2], %[temp2],   %[temp3]   \n\t"
174    "addiu  %[pX],    %[pX],      8          \n\t"
175    "addiu  %[pY],    %[pY],      8          \n\t"
176    "madd   %[i],     %[temp0]               \n\t"
177    "madd   %[i],     %[temp2]               \n\t"
178    "bne    %[pX],    %[LoopEnd], 1b         \n\t"
179  "2:                                        \n\t"
180    "mfhi   %[temp0]                         \n\t"
181    "mflo   %[temp1]                         \n\t"
182    : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
183      [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),
184      [i]"=&r"(i), [pX]"+r"(pX), [pY]"+r"(pY)
185    : [LoopEnd]"r"(LoopEnd)
186    : "memory", "hi", "lo"
187  );
188
189  return (double)((int64_t)temp0 << 32 | temp1);
190}
191
192#define HUFFMAN_COST_PASS                                 \
193  __asm__ volatile(                                       \
194    "sll   %[temp1],  %[temp0],    3           \n\t"      \
195    "addiu %[temp3],  %[streak],   -3          \n\t"      \
196    "addu  %[temp2],  %[pstreaks], %[temp1]    \n\t"      \
197    "blez  %[temp3],  1f                       \n\t"      \
198    "srl   %[temp1],  %[temp1],    1           \n\t"      \
199    "addu  %[temp3],  %[pcnts],    %[temp1]    \n\t"      \
200    "lw    %[temp0],  4(%[temp2])              \n\t"      \
201    "lw    %[temp1],  0(%[temp3])              \n\t"      \
202    "addu  %[temp0],  %[temp0],    %[streak]   \n\t"      \
203    "addiu %[temp1],  %[temp1],    1           \n\t"      \
204    "sw    %[temp0],  4(%[temp2])              \n\t"      \
205    "sw    %[temp1],  0(%[temp3])              \n\t"      \
206    "b     2f                                  \n\t"      \
207  "1:                                          \n\t"      \
208    "lw    %[temp0],  0(%[temp2])              \n\t"      \
209    "addu  %[temp0],  %[temp0],    %[streak]   \n\t"      \
210    "sw    %[temp0],  0(%[temp2])              \n\t"      \
211  "2:                                          \n\t"      \
212    : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),           \
213      [temp3]"=&r"(temp3), [temp0]"+r"(temp0)             \
214    : [pstreaks]"r"(pstreaks), [pcnts]"r"(pcnts),         \
215      [streak]"r"(streak)                                 \
216    : "memory"                                            \
217  );
218
219// Returns the various RLE counts
220static VP8LStreaks HuffmanCostCount(const uint32_t* population, int length) {
221  int i;
222  int streak = 0;
223  VP8LStreaks stats;
224  int* const pstreaks = &stats.streaks[0][0];
225  int* const pcnts = &stats.counts[0];
226  int temp0, temp1, temp2, temp3;
227  memset(&stats, 0, sizeof(stats));
228  for (i = 0; i < length - 1; ++i) {
229    ++streak;
230    if (population[i] == population[i + 1]) {
231      continue;
232    }
233    temp0 = (population[i] != 0);
234    HUFFMAN_COST_PASS
235    streak = 0;
236  }
237  ++streak;
238  temp0 = (population[i] != 0);
239  HUFFMAN_COST_PASS
240
241  return stats;
242}
243
244static VP8LStreaks HuffmanCostCombinedCount(const uint32_t* X,
245                                            const uint32_t* Y, int length) {
246  int i;
247  int streak = 0;
248  VP8LStreaks stats;
249  int* const pstreaks = &stats.streaks[0][0];
250  int* const pcnts = &stats.counts[0];
251  int temp0, temp1, temp2, temp3;
252  memset(&stats, 0, sizeof(stats));
253  for (i = 0; i < length - 1; ++i) {
254    const uint32_t xy = X[i] + Y[i];
255    const uint32_t xy_next = X[i + 1] + Y[i + 1];
256    ++streak;
257    if (xy == xy_next) {
258      continue;
259    }
260    temp0 = (xy != 0);
261    HUFFMAN_COST_PASS
262    streak = 0;
263  }
264  {
265    const uint32_t xy = X[i] + Y[i];
266    ++streak;
267    temp0 = (xy != 0);
268    HUFFMAN_COST_PASS
269  }
270
271  return stats;
272}
273
274#define ASM_START                                       \
275  __asm__ volatile(                                     \
276    ".set   push                            \n\t"       \
277    ".set   at                              \n\t"       \
278    ".set   macro                           \n\t"       \
279  "1:                                       \n\t"
280
281// P2 = P0 + P1
282// A..D - offsets
283// E - temp variable to tell macro
284//     if pointer should be incremented
285// literal_ and successive histograms could be unaligned
286// so we must use ulw and usw
287#define ADD_TO_OUT(A, B, C, D, E, P0, P1, P2)           \
288    "ulw    %[temp0], "#A"(%["#P0"])        \n\t"       \
289    "ulw    %[temp1], "#B"(%["#P0"])        \n\t"       \
290    "ulw    %[temp2], "#C"(%["#P0"])        \n\t"       \
291    "ulw    %[temp3], "#D"(%["#P0"])        \n\t"       \
292    "ulw    %[temp4], "#A"(%["#P1"])        \n\t"       \
293    "ulw    %[temp5], "#B"(%["#P1"])        \n\t"       \
294    "ulw    %[temp6], "#C"(%["#P1"])        \n\t"       \
295    "ulw    %[temp7], "#D"(%["#P1"])        \n\t"       \
296    "addu   %[temp4], %[temp4],   %[temp0]  \n\t"       \
297    "addu   %[temp5], %[temp5],   %[temp1]  \n\t"       \
298    "addu   %[temp6], %[temp6],   %[temp2]  \n\t"       \
299    "addu   %[temp7], %[temp7],   %[temp3]  \n\t"       \
300    "addiu  %["#P0"],  %["#P0"],  16        \n\t"       \
301  ".if "#E" == 1                            \n\t"       \
302    "addiu  %["#P1"],  %["#P1"],  16        \n\t"       \
303  ".endif                                   \n\t"       \
304    "usw    %[temp4], "#A"(%["#P2"])        \n\t"       \
305    "usw    %[temp5], "#B"(%["#P2"])        \n\t"       \
306    "usw    %[temp6], "#C"(%["#P2"])        \n\t"       \
307    "usw    %[temp7], "#D"(%["#P2"])        \n\t"       \
308    "addiu  %["#P2"], %["#P2"],   16        \n\t"       \
309    "bne    %["#P0"], %[LoopEnd], 1b        \n\t"       \
310    ".set   pop                             \n\t"       \
311
312#define ASM_END_COMMON_0                                \
313    : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),         \
314      [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),         \
315      [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),         \
316      [temp6]"=&r"(temp6), [temp7]"=&r"(temp7),         \
317      [pa]"+r"(pa), [pout]"+r"(pout)
318
319#define ASM_END_COMMON_1                                \
320    : [LoopEnd]"r"(LoopEnd)                             \
321    : "memory", "at"                                    \
322  );
323
324#define ASM_END_0                                       \
325    ASM_END_COMMON_0                                    \
326      , [pb]"+r"(pb)                                    \
327    ASM_END_COMMON_1
328
329#define ASM_END_1                                       \
330    ASM_END_COMMON_0                                    \
331    ASM_END_COMMON_1
332
333#define ADD_VECTOR(A, B, OUT, SIZE, EXTRA_SIZE)  do {   \
334  const uint32_t* pa = (const uint32_t*)(A);            \
335  const uint32_t* pb = (const uint32_t*)(B);            \
336  uint32_t* pout = (uint32_t*)(OUT);                    \
337  const uint32_t* const LoopEnd = pa + (SIZE);          \
338  assert((SIZE) % 4 == 0);                              \
339  ASM_START                                             \
340  ADD_TO_OUT(0, 4, 8, 12, 1, pa, pb, pout)              \
341  ASM_END_0                                             \
342  if ((EXTRA_SIZE) > 0) {                               \
343    const int last = (EXTRA_SIZE);                      \
344    int i;                                              \
345    for (i = 0; i < last; ++i) pout[i] = pa[i] + pb[i]; \
346  }                                                     \
347} while (0)
348
349#define ADD_VECTOR_EQ(A, OUT, SIZE, EXTRA_SIZE)  do {   \
350  const uint32_t* pa = (const uint32_t*)(A);            \
351  uint32_t* pout = (uint32_t*)(OUT);                    \
352  const uint32_t* const LoopEnd = pa + (SIZE);          \
353  assert((SIZE) % 4 == 0);                              \
354  ASM_START                                             \
355  ADD_TO_OUT(0, 4, 8, 12, 0, pa, pout, pout)            \
356  ASM_END_1                                             \
357  if ((EXTRA_SIZE) > 0) {                               \
358    const int last = (EXTRA_SIZE);                      \
359    int i;                                              \
360    for (i = 0; i < last; ++i) pout[i] += pa[i];        \
361  }                                                     \
362} while (0)
363
364static void HistogramAdd(const VP8LHistogram* const a,
365                         const VP8LHistogram* const b,
366                         VP8LHistogram* const out) {
367  uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
368  const int extra_cache_size = VP8LHistogramNumCodes(a->palette_code_bits_)
369                             - (NUM_LITERAL_CODES + NUM_LENGTH_CODES);
370  assert(a->palette_code_bits_ == b->palette_code_bits_);
371
372  if (b != out) {
373    ADD_VECTOR(a->literal_, b->literal_, out->literal_,
374               NUM_LITERAL_CODES + NUM_LENGTH_CODES, extra_cache_size);
375    ADD_VECTOR(a->distance_, b->distance_, out->distance_,
376               NUM_DISTANCE_CODES, 0);
377    ADD_VECTOR(a->red_, b->red_, out->red_, NUM_LITERAL_CODES, 0);
378    ADD_VECTOR(a->blue_, b->blue_, out->blue_, NUM_LITERAL_CODES, 0);
379    ADD_VECTOR(a->alpha_, b->alpha_, out->alpha_, NUM_LITERAL_CODES, 0);
380  } else {
381    ADD_VECTOR_EQ(a->literal_, out->literal_,
382                  NUM_LITERAL_CODES + NUM_LENGTH_CODES, extra_cache_size);
383    ADD_VECTOR_EQ(a->distance_, out->distance_, NUM_DISTANCE_CODES, 0);
384    ADD_VECTOR_EQ(a->red_, out->red_, NUM_LITERAL_CODES, 0);
385    ADD_VECTOR_EQ(a->blue_, out->blue_, NUM_LITERAL_CODES, 0);
386    ADD_VECTOR_EQ(a->alpha_, out->alpha_, NUM_LITERAL_CODES, 0);
387  }
388}
389
390#undef ADD_VECTOR_EQ
391#undef ADD_VECTOR
392#undef ASM_END_1
393#undef ASM_END_0
394#undef ASM_END_COMMON_1
395#undef ASM_END_COMMON_0
396#undef ADD_TO_OUT
397#undef ASM_START
398
399#endif  // WEBP_USE_MIPS32
400
401//------------------------------------------------------------------------------
402// Entry point
403
404extern void VP8LDspInitMIPS32(void);
405
406void VP8LDspInitMIPS32(void) {
407#if defined(WEBP_USE_MIPS32)
408  VP8LFastSLog2Slow = FastSLog2Slow;
409  VP8LFastLog2Slow = FastLog2Slow;
410  VP8LExtraCost = ExtraCost;
411  VP8LExtraCostCombined = ExtraCostCombined;
412  VP8LHuffmanCostCount = HuffmanCostCount;
413  VP8LHuffmanCostCombinedCount = HuffmanCostCombinedCount;
414  VP8LHistogramAdd = HistogramAdd;
415#endif  // WEBP_USE_MIPS32
416}
417