stdlib.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // clang --target=wasm32 -c -emit-llvm -O3 -fno-builtin -Wall stdlib.c
  2. #include <stdint.h>
  3. #include <stddef.h>
  4. #include <stdbool.h>
  5. /*
  6. * The external interface
  7. */
  8. /*
  9. * Retrieve contract storage for this account. If nothing is stored at the key,
  10. * set the memory at dest to 0. If the storage is shorter, pad the remaining bytes
  11. * with 0.
  12. */
  13. extern void get_storage32(uint32_t key, void *dest, int32_t length);
  14. extern void set_storage32(uint32_t key, void *src, int32_t length);
  15. /*
  16. */
  17. __attribute__((visibility("hidden")))
  18. void __memset8(void *_dest, uint64_t val, size_t length)
  19. {
  20. uint64_t *dest = _dest;
  21. do {
  22. *dest++ = val;
  23. } while (--length);
  24. }
  25. /*
  26. * Our memcpy can only deal with multiples of 8 bytes. This is enough for
  27. * simple allocator below.
  28. */
  29. __attribute__((visibility("hidden")))
  30. void __memcpy8(void *_dest, void *_src, size_t length)
  31. {
  32. uint64_t *dest = _dest;
  33. uint64_t *src = _src;
  34. do {
  35. *dest++ = *src++;
  36. } while (--length);
  37. }
  38. /*
  39. * Fast-ish clear, 8 bytes at a time.
  40. */
  41. __attribute__((visibility("hidden")))
  42. void __bzero8(void *_dest, size_t length)
  43. {
  44. uint64_t *dest = _dest;
  45. do
  46. *dest++ = 0;
  47. while (--length);
  48. }
  49. /*
  50. * Fast-ish set, 8 bytes at a time.
  51. */
  52. __attribute__((visibility("hidden")))
  53. void __bset8(void *_dest, size_t length)
  54. {
  55. int64_t *dest = _dest;
  56. do
  57. *dest++ = -1;
  58. while (--length);
  59. }
  60. /*
  61. There are many tradeoffs in heaps. I think for Solidity, we want:
  62. - small code size to reduce code length
  63. - not many malloc objects
  64. - not much memory
  65. So I think we should avoid fragmentation by neighbour merging. The most
  66. costly is walking the doubly linked list looking for free space.
  67. */
  68. struct chunk {
  69. struct chunk *next, *prev;
  70. size_t length;
  71. bool allocated;
  72. };
  73. __attribute__((visibility("hidden")))
  74. void __init_heap()
  75. {
  76. struct chunk *first = (struct chunk*)0x10000;
  77. first->next = first->prev = NULL;
  78. first->allocated = false;
  79. first->length = (size_t)
  80. (__builtin_wasm_memory_size(0) -
  81. (size_t)first - sizeof(struct chunk));
  82. }
  83. __attribute__((visibility("hidden")))
  84. void __attribute__((noinline)) __free(void *m)
  85. {
  86. struct chunk *cur = m;
  87. cur--;
  88. if (m) {
  89. cur->allocated = false;
  90. struct chunk *next = cur->next;
  91. if (next && !next->allocated) {
  92. // merge with next
  93. if ((cur->next = next->next) != NULL)
  94. cur->next->prev = cur;
  95. cur->length += next->length + sizeof(struct chunk);
  96. next = cur->next;
  97. }
  98. struct chunk *prev = cur->prev;
  99. if (prev && !prev->allocated) {
  100. // merge with previous
  101. prev->next = next;
  102. next->prev = prev;
  103. prev->length += cur->length + sizeof(struct chunk);
  104. }
  105. }
  106. }
  107. __attribute__((visibility("hidden")))
  108. static void shrink_chunk(struct chunk *cur, size_t size)
  109. {
  110. // round up to nearest 8 bytes
  111. size = (size + 7) & ~7;
  112. if (cur->length - size >= (8 + sizeof(struct chunk))) {
  113. // split and return
  114. void *data = (cur + 1);
  115. struct chunk *new = data + size;
  116. if ((new->next = cur->next) != NULL)
  117. new->next->prev = new;
  118. cur->next = new;
  119. new->prev = cur;
  120. new->allocated = false;
  121. new->length = cur->length - size - sizeof(struct chunk);
  122. cur->length = size;
  123. }
  124. }
  125. __attribute__((visibility("hidden")))
  126. void* __attribute__((noinline)) __malloc(size_t size)
  127. {
  128. struct chunk *cur = (struct chunk*)0x10000;
  129. while (cur && (cur->allocated || size > cur->length))
  130. cur = cur->next;
  131. if (cur) {
  132. shrink_chunk(cur, size);
  133. cur->allocated = true;
  134. return ++cur;
  135. } else {
  136. // go bang
  137. __builtin_unreachable();
  138. }
  139. }
  140. __attribute__((visibility("hidden")))
  141. void* __realloc(void *m, size_t size)
  142. {
  143. struct chunk *cur = m;
  144. cur--;
  145. struct chunk *next = cur->next;
  146. if (next && !next->allocated && size <=
  147. (cur->length + next->length + sizeof(struct chunk))) {
  148. // merge with next
  149. cur->next = next->next;
  150. cur->next->prev = cur;
  151. cur->length += next->length + sizeof(struct chunk);
  152. // resplit ..
  153. shrink_chunk(cur, size);
  154. return m;
  155. } else {
  156. void *n = __malloc(size);
  157. __memcpy8(n, m, size / 8);
  158. __free(m);
  159. return n;
  160. }
  161. }
  162. // This function is used for abi decoding integers.
  163. // ABI encoding is big endian, and can have integers of 8 to 256 bits
  164. // (1 to 32 bytes). This function copies length bytes and reverses the
  165. // order since wasm is little endian.
  166. __attribute__((visibility("hidden")))
  167. void __be32toleN(uint8_t *from, uint8_t *to, uint32_t length)
  168. {
  169. from += 31;
  170. do {
  171. *to++ = *from--;
  172. } while (--length);
  173. }
  174. // This function is for used for abi encoding integers
  175. // ABI encoding is big endian.
  176. __attribute__((visibility("hidden")))
  177. void __leNtobe32(uint8_t *from, uint8_t *to, uint32_t length)
  178. {
  179. to += 31;
  180. do {
  181. *to-- = *from++;
  182. } while (--length);
  183. }