PostgreSQL Source Code git master
Loading...
Searching...
No Matches
llvmjit_deform.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * llvmjit_deform.c
4 * Generate code for deforming a heap tuple.
5 *
6 * This gains performance benefits over unJITed deforming from compile-time
7 * knowledge of the tuple descriptor. Fixed column widths, NOT NULLness, etc
8 * can be taken advantage of.
9 *
10 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
11 * Portions Copyright (c) 1994, Regents of the University of California
12 *
13 * IDENTIFICATION
14 * src/backend/jit/llvm/llvmjit_deform.c
15 *
16 *-------------------------------------------------------------------------
17 */
18
19#include "postgres.h"
20
21#include <llvm-c/Core.h>
22
23#include "access/htup_details.h"
25#include "executor/tuptable.h"
26#include "jit/llvmjit.h"
27#include "jit/llvmjit_emit.h"
28
29
30/*
31 * Create a function that deforms a tuple of type desc up to natts columns.
32 */
35 const TupleTableSlotOps *ops, int natts)
36{
37 char *funcname;
38
42
45
49
58
60
68
70
76
78
80
81 /* last column (0 indexed) guaranteed to exist */
83
84 /* current known alignment */
85 int known_alignment = 0;
86
87 /* if true, known_alignment describes definite offset of column */
88 bool attguaranteedalign = true;
89
90 int attnum;
91
92 /* virtual tuples never need deforming, so don't generate code */
93 if (ops == &TTSOpsVirtual)
94 return NULL;
95
96 /* decline to JIT for slot types we don't know to handle */
97 if (ops != &TTSOpsHeapTuple && ops != &TTSOpsBufferHeapTuple &&
98 ops != &TTSOpsMinimalTuple)
99 return NULL;
100
101 mod = llvm_mutable_module(context);
103
104 funcname = llvm_expand_funcname(context, "deform");
105
106 /*
107 * Check which columns have to exist in all tuples, so we don't have to
108 * check the row's natts unnecessarily.
109 */
110 for (attnum = 0; attnum < desc->natts; attnum++)
111 {
114
115 /*
116 * If the column is declared NOT NULL then it must be present in every
117 * tuple, unless there's a "missing" entry that could provide a
118 * non-NULL value for it. That in turn guarantees that the NULL bitmap
119 * - if there are any NULLable columns - is at least long enough to
120 * cover columns up to attnum. We treat virtual generated columns
121 * similar to atthasmissing columns, as these columns could either not
122 * be represented in the tuple or could have the column represented as
123 * a NULL in the null bitmap.
124 *
125 * Be paranoid and also check !attisdropped, even though the
126 * combination of attisdropped && attnotnull combination shouldn't
127 * exist.
128 */
129 if (catt->attnullability == ATTNULLABLE_VALID &&
130 !catt->atthasmissing &&
131 !catt->attisdropped &&
132 attr->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
134 }
135
136 /* Create the signature and function */
137 {
138 LLVMTypeRef param_types[1];
139
140 param_types[0] = l_ptr(StructTupleTableSlot);
141
143 param_types, lengthof(param_types), 0);
144 }
149
150 b_entry =
153 LLVMAppendBasicBlockInContext(lc, v_deform_fn, "adjust_unavail_cols");
155 LLVMAppendBasicBlockInContext(lc, v_deform_fn, "find_startblock");
156 b_out =
158 b_dead =
160
162
169
170 known_alignment = 0;
171
173
174 /* perform allocas first, llvm only converts those to registers */
175 v_offp = LLVMBuildAlloca(b, TypeSizeT, "v_offp");
176
178
181 "tts_values");
184 "tts_ISNULL");
186
187 if (ops == &TTSOpsHeapTuple || ops == &TTSOpsBufferHeapTuple)
188 {
190
191 v_heapslot =
193 v_slot,
195 "heapslot");
199 "tupleheader");
200 }
201 else if (ops == &TTSOpsMinimalTuple)
202 {
204
207 v_slot,
209 "minimalslot");
219 "tupleheader");
220 }
221 else
222 {
223 /* should've returned at the start of the function */
225 }
226
227 v_tuplep =
232 "tuple");
233 v_bits =
237 v_tuplep,
239 ""),
241 "t_bits");
245 v_tuplep,
247 "infomask1");
252 "infomask2");
253
254 /* t_infomask & HEAP_HASNULL */
255 v_hasnulls =
259 v_infomask1, ""),
260 l_int16_const(lc, 0),
261 "hasnulls");
262
263 /* t_infomask2 & HEAP_NATTS_MASK */
267 "maxatt");
268
269 /*
270 * Need to zext, as getelementptr otherwise treats hoff as a signed 8bit
271 * integer, which'd yield a negative offset for t_hoff > 127.
272 */
273 v_hoff =
277 v_tuplep,
279 ""),
280 LLVMInt32TypeInContext(lc), "t_hoff");
281
285 v_tuplep,
287 ""),
288 &v_hoff, 1,
289 "v_tupdata_base");
290
291 /*
292 * Load tuple start offset from slot. Will be reset below in case there's
293 * no existing deformed columns in slot.
294 */
295 {
297
301 }
302
303 /* build the basic block for each attribute, need them as jump target */
304 for (attnum = 0; attnum < natts; attnum++)
305 {
307 l_bb_append_v(v_deform_fn, "block.attr.%d.attcheckattno", attnum);
309 l_bb_append_v(v_deform_fn, "block.attr.%d.start", attnum);
311 l_bb_append_v(v_deform_fn, "block.attr.%d.attisnull", attnum);
313 l_bb_append_v(v_deform_fn, "block.attr.%d.attcheckalign", attnum);
315 l_bb_append_v(v_deform_fn, "block.attr.%d.align", attnum);
317 l_bb_append_v(v_deform_fn, "block.attr.%d.store", attnum);
318 }
319
320 /*
321 * Check if it is guaranteed that all the desired attributes are available
322 * in the tuple (but still possibly NULL), by dint of either the last
323 * to-be-deformed column being NOT NULL, or subsequent ones not accessed
324 * here being NOT NULL. If that's not guaranteed the tuple headers natt's
325 * has to be checked, and missing attributes potentially have to be
326 * fetched (using slot_getmissingattrs().
327 */
328 if ((natts - 1) <= guaranteed_column_number)
329 {
330 /* just skip through unnecessary blocks */
334 }
335 else
336 {
338 LLVMValueRef f;
339
340 /* branch if not all columns available */
343 v_maxatt,
344 l_int16_const(lc, natts),
345 ""),
348
349 /* if not, memset tts_isnull of relevant cols to true */
351
352 v_params[0] = v_slot;
354 v_params[2] = l_int32_const(lc, natts);
355 f = llvm_pg_func(mod, "slot_getmissingattrs");
356 l_call(b,
360 }
361
363
365
366 /*
367 * Build switch to go from nvalid to the right startblock. Callers
368 * currently don't have the knowledge, but it'd be good for performance to
369 * avoid this check when it's known that the slot is empty (e.g. in scan
370 * nodes).
371 */
372 if (true)
373 {
375 b_dead, natts);
376
377 for (attnum = 0; attnum < natts; attnum++)
378 {
380
382 }
383 }
384 else
385 {
386 /* jump from entry block to first block */
388 }
389
392
393 /*
394 * Iterate over each attribute that needs to be deformed, build code to
395 * deform it.
396 */
397 for (attnum = 0; attnum < natts; attnum++)
398 {
401
403 int alignto = att->attalignby;
407
408 /* build block checking whether we did all the necessary attributes */
410
411 /*
412 * If this is the first attribute, slot->tts_nvalid was 0. Therefore
413 * also reset offset to 0, it may be from a previous execution.
414 */
415 if (attnum == 0)
416 {
418 }
419
420 /*
421 * Build check whether column is available (i.e. whether the tuple has
422 * that many columns stored). We can avoid the branch if we know
423 * there's a subsequent NOT NULL column.
424 */
426 {
428 }
429 else
430 {
432
434 l_attno,
435 v_maxatt,
436 "heap_natts");
438 }
440
441 /*
442 * Check for nulls if necessary. No need to take missing attributes
443 * into account, because if they're present the heaptuple's natts
444 * would have indicated that a slot_getmissingattrs() is needed. When
445 * present in the tuple, virtual generated columns are always stored
446 * as NULL, so we must always perform NULL checks for these.
447 */
448 if (att->attnullability != ATTNULLABLE_VALID ||
449 attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
450 {
459
462
463 if (attnum + 1 == natts)
464 b_next = b_out;
465 else
467
469 v_nullbytemask = l_int8_const(lc, 1 << ((attnum) & 0x07));
471
473 LLVMIntEQ,
475 l_int8_const(lc, 0),
476 "attisnull");
477
479
481
483
484 /* store null-byte */
486 l_int8_const(lc, 1),
488 /* store zero datum */
490 l_datum_const(0),
491 l_gep(b, TypeDatum, v_tts_values, &l_attno, 1, ""));
492
494 attguaranteedalign = false;
495 }
496 else
497 {
498 /* nothing to do */
502 }
504
505 /* ------
506 * Even if alignment is required, we can skip doing it if provably
507 * unnecessary:
508 * - first column is guaranteed to be aligned
509 * - columns following a NOT NULL fixed width datum have known
510 * alignment, can skip alignment computation if that known alignment
511 * is compatible with current column.
512 * ------
513 */
514 if (alignto > 1 &&
516 {
517 /*
518 * When accessing a varlena field, we have to "peek" to see if we
519 * are looking at a pad byte or the first byte of a 1-byte-header
520 * datum. A zero byte must be either a pad byte, or the first
521 * byte of a correctly aligned 4-byte length word; in either case,
522 * we can align safely. A non-zero byte must be either a 1-byte
523 * length word, or the first byte of a correctly aligned 4-byte
524 * length word; in either case, we need not align.
525 */
526 if (att->attlen == -1)
527 {
531
532 /* don't know if short varlena or not */
533 attguaranteedalign = false;
534
535 v_off = l_load(b, TypeSizeT, v_offp, "");
536
539 v_ispad =
542 "ispadbyte");
546 }
547 else
548 {
550 }
551
553
554 /* translation of alignment code (cf TYPEALIGN()) */
555 {
558
559 /* ((ALIGNVAL) - 1) */
561
562 /* ((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) */
564
565 /* ~((uintptr_t) ((ALIGNVAL) - 1)) */
567
568 v_off_aligned = LLVMBuildAnd(b, v_lh, v_rh, "aligned_offset");
569
571 }
572
573 /*
574 * As alignment either was unnecessary or has been performed, we
575 * now know the current alignment. This is only safe because this
576 * value isn't used for varlena and nullable columns.
577 */
578 if (known_alignment >= 0)
579 {
582 }
583
586 }
587 else
588 {
593 }
595
596 /*
597 * Store the current offset if known to be constant. That allows LLVM
598 * to generate better code. Without that LLVM can't figure out that
599 * the offset might be constant due to the jumps for previously
600 * decoded columns.
601 */
603 {
606 }
607
608 /* compute what following columns are aligned to */
609 if (att->attlen < 0)
610 {
611 /* can't guarantee any alignment after variable length field */
612 known_alignment = -1;
613 attguaranteedalign = false;
614 }
615 else if (att->attnullability == ATTNULLABLE_VALID &&
617 {
618 /*
619 * If the offset to the column was previously known, a NOT NULL &
620 * fixed-width column guarantees that alignment is just the
621 * previous alignment plus column width.
622 */
623 Assert(att->attlen > 0);
624 known_alignment += att->attlen;
625 }
626 else if (att->attnullability == ATTNULLABLE_VALID &&
627 attr->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL &&
628 (att->attlen % alignto) == 0)
629 {
630 /*
631 * After a NOT NULL (and not virtual generated) fixed-width column
632 * with a length that is a multiple of its alignment requirement,
633 * we know the following column is aligned to at least the current
634 * column's alignment.
635 */
636 Assert(att->attlen > 0);
639 attguaranteedalign = false;
640 }
641 else
642 {
643 known_alignment = -1;
644 attguaranteedalign = false;
645 }
646
647
648 /* compute address to load data from */
649 {
651
652 v_attdatap =
654 }
655
656 /* compute address to store value at */
658
659 /* store null-byte (false) */
662
663 /*
664 * Store datum. For byval: datums copy the value, extend to Datum's
665 * width, and store. For byref types: store pointer to data.
666 */
667 if (att->attbyval)
668 {
670 LLVMTypeRef vartype = LLVMIntTypeInContext(lc, att->attlen * 8);
672
675 v_tmp_loaddata = l_load(b, vartype, v_tmp_loaddata, "attr_byval");
677
679 }
680 else
681 {
683
684 /* store pointer */
688 TypeDatum,
689 "attr_ptr");
691 }
692
693 /* increment data pointer */
694 if (att->attlen > 0)
695 {
697 }
698 else if (att->attlen == -1)
699 {
700 v_incby = l_call(b,
701 llvm_pg_var_func_type("varsize_any"),
702 llvm_pg_func(mod, "varsize_any"),
703 &v_attdatap, 1,
704 "varsize_any");
707 }
708 else if (att->attlen == -2)
709 {
710 v_incby = l_call(b,
711 llvm_pg_var_func_type("strlen"),
712 llvm_pg_func(mod, "strlen"),
713 &v_attdatap, 1, "strlen");
714
716
717 /* add 1 for NUL byte */
719 }
720 else
721 {
722 Assert(false);
723 v_incby = NULL; /* silence compiler */
724 }
725
727 {
730 }
731 else
732 {
734
735 v_off = LLVMBuildAdd(b, v_off, v_incby, "increment_offset");
737 }
738
739 /*
740 * jump to next block, unless last possible column, or all desired
741 * (available) attributes have been fetched.
742 */
743 if (attnum + 1 == natts)
744 {
745 /* jump out */
747 }
748 else
749 {
751 }
752 }
753
754
755 /* build block that returns */
757
758 {
760
765 }
766
768
769 return v_deform_fn;
770}
#define TYPEALIGN(ALIGNVAL, LEN)
Definition c.h:889
#define Assert(condition)
Definition c.h:943
#define pg_unreachable()
Definition c.h:367
#define lengthof(array)
Definition c.h:873
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
const TupleTableSlotOps TTSOpsBufferHeapTuple
Definition execTuples.c:87
const TupleTableSlotOps TTSOpsHeapTuple
Definition execTuples.c:85
const TupleTableSlotOps TTSOpsMinimalTuple
Definition execTuples.c:86
#define palloc_array(type, count)
Definition fe_memutils.h:91
#define FIELDNO_HEAPTUPLEDATA_DATA
Definition htup.h:67
#define HEAP_NATTS_MASK
#define HEAP_HASNULL
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK
#define FIELDNO_HEAPTUPLEHEADERDATA_HOFF
#define FIELDNO_HEAPTUPLEHEADERDATA_BITS
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK2
#define funcname
int b
Definition isn.c:74
LLVMTypeRef StructMinimalTupleTableSlot
Definition llvmjit.c:68
LLVMValueRef llvm_pg_func(LLVMModuleRef mod, const char *funcname)
Definition llvmjit.c:465
LLVMTypeRef TypeSizeT
Definition llvmjit.c:56
char * llvm_expand_funcname(struct LLVMJitContext *context, const char *basename)
Definition llvmjit.c:342
LLVMTypeRef llvm_pg_var_func_type(const char *varname)
Definition llvmjit.c:443
LLVMTypeRef StructTupleTableSlot
Definition llvmjit.c:65
LLVMTypeRef TypeStorageBool
Definition llvmjit.c:59
LLVMTypeRef TypeDatum
Definition llvmjit.c:57
LLVMTypeRef StructHeapTupleTableSlot
Definition llvmjit.c:67
LLVMModuleRef llvm_mutable_module(LLVMJitContext *context)
Definition llvmjit.c:317
LLVMValueRef AttributeTemplate
Definition llvmjit.c:79
LLVMTypeRef StructHeapTupleHeaderData
Definition llvmjit.c:66
LLVMTypeRef StructHeapTupleData
Definition llvmjit.c:62
void llvm_copy_attributes(LLVMValueRef v_from, LLVMValueRef v_to)
Definition llvmjit.c:517
LLVMValueRef slot_compile_deform(LLVMJitContext *context, TupleDesc desc, const TupleTableSlotOps *ops, int natts)
LLVMTypeRef LLVMGetFunctionType(LLVMValueRef r)
int16 attnum
FormData_pg_attribute * Form_pg_attribute
static int fb(int x)
uint8 attalignby
Definition tupdesc.h:74
char attnullability
Definition tupdesc.h:80
#define ATTNULLABLE_VALID
Definition tupdesc.h:86
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
#define FIELDNO_HEAPTUPLETABLESLOT_OFF
Definition tuptable.h:280
#define FIELDNO_HEAPTUPLETABLESLOT_TUPLE
Definition tuptable.h:278
#define FIELDNO_TUPLETABLESLOT_ISNULL
Definition tuptable.h:132
#define FIELDNO_MINIMALTUPLETABLESLOT_TUPLE
Definition tuptable.h:314
#define FIELDNO_MINIMALTUPLETABLESLOT_OFF
Definition tuptable.h:318
#define FIELDNO_TUPLETABLESLOT_VALUES
Definition tuptable.h:130
#define FIELDNO_TUPLETABLESLOT_NVALID
Definition tuptable.h:125