Merge branch 'riscv'
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..69f8f40
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,31 @@
+# Read the Docs configuration file for Sphinx projects
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the OS, Python version and other tools you might need
+build:
+  os: ubuntu-22.04
+  tools:
+    python: "3.11"
+
+# Build documentation in the "Documentation/" directory with Sphinx
+sphinx:
+  configuration: Documentation/conf.py
+  # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
+  # builder: "dirhtml"
+  # Fail on all warnings to avoid broken references
+  # fail_on_warning: true
+
+# Optionally build your docs in additional formats such as PDF and ePub
+# formats:
+#   - pdf
+#   - epub
+
+# Optional but recommended, declare the Python requirements required
+# to build your documentation
+# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
+python:
+  install:
+    - requirements: Documentation/requirements.txt
diff --git a/Documentation/conf.py b/Documentation/conf.py
index affa907..e3215b2 100644
--- a/Documentation/conf.py
+++ b/Documentation/conf.py
@@ -21,7 +21,7 @@
 
 # -- General configuration ------------------------------------------------
 
-needs_sphinx = '1.7'
+needs_sphinx = '1.8'
 
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -78,7 +78,7 @@
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = None
+language = 'en'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
@@ -115,12 +115,6 @@
 # so a file named "default.css" will overwrite the builtin "default.css".
 html_static_path = ['sphinx/static']
 
-html_context = {
-    'css_files': [
-        '_static/theme_overrides.css',
-    ],
-}
-
 # Custom sidebar templates, must be a dictionary that maps document names
 # to template names.
 #
diff --git a/Documentation/requirements.txt b/Documentation/requirements.txt
new file mode 100644
index 0000000..02bf565
--- /dev/null
+++ b/Documentation/requirements.txt
@@ -0,0 +1 @@
+recommonmark
diff --git a/Makefile b/Makefile
index 84b4527..e172758 100644
--- a/Makefile
+++ b/Makefile
@@ -200,7 +200,7 @@
 ifeq (${MULTIARCH_TRIPLET},x86_64-linux-gnux32)
 arch := x32
 endif
-ifneq ($(filter ${arch},i386 i486 i586 i686 x86_64 amd64),)
+ifneq ($(filter ${arch},i386 i486 i586 i686 x86_64 amd64 aarch64 arm64),)
 LLVM_VERSION:=$(shell $(LLVM_CONFIG) --version)
 LLVM_VERSION_MAJOR:=$(firstword $(subst ., ,$(LLVM_VERSION)))
 ifeq ($(shell expr "$(LLVM_VERSION_MAJOR)" '>=' 3),1)
@@ -216,6 +216,9 @@
 sparse-llvm-cflags := $(LLVM_CFLAGS)
 sparse-llvm-ldflags := $(LLVM_LDFLAGS)
 sparse-llvm-ldlibs := $(LLVM_LIBS)
+ifeq ($(LLVM_VERSION_MAJOR),14)
+sparse-llvm-cflags += -Wno-deprecated-declarations
+endif
 else
 $(warning LLVM 3.0 or later required. Your system has version $(LLVM_VERSION) installed.)
 endif
diff --git a/linearize.c b/linearize.c
index d9aed61..1db2d50 100644
--- a/linearize.c
+++ b/linearize.c
@@ -91,7 +91,7 @@
 
 	if (!bb)
 		return ".L???";
-	snprintf(buf, 64, ".L%u", bb->nr);
+	snprintf(buf, 16, ".L%u", bb->nr);
 	return buf;
 }
 
diff --git a/simplify.c b/simplify.c
index 0353642..3c4ace3 100644
--- a/simplify.c
+++ b/simplify.c
@@ -1818,10 +1818,12 @@
 		insn->src2 = eval_op(insn->opcode, insn->size, insn->src2, def->src2);
 		return replace_pseudo(insn, &insn->src1, def->src1);
 	}
-	if (!one_use(def->target))
-		return 0;
-	switch_pseudo(def, &def->src1, insn, &insn->src2);
-	return REPEAT_CSE;
+
+	if (!canonical_order(def->src1, insn->src2) && can_move_to(insn->src2, def)) {
+		// (x # y) # z -> (z # y) # x  when x ≻ z
+		return switch_pseudo(def, &def->src1, insn, &insn->src2);
+	}
+	return 0;
 }
 
 static int simplify_add_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2)
diff --git a/sparse-llvm.c b/sparse-llvm.c
index 9ceb19a..90a931b 100644
--- a/sparse-llvm.c
+++ b/sparse-llvm.c
@@ -29,9 +29,25 @@
 
 static LLVMTypeRef func_return_type(struct symbol *sym)
 {
+	if (sym->type == SYM_NODE)
+		sym = sym->ctype.base_type;
 	return symbol_type(sym->ctype.base_type);
 }
 
+#if LLVM_VERSION_MAJOR > 14
+// A call can be done either with a SYM_FN or a SYM_PTR (pointing to a SYM_FN).
+// Return the type corresponding to the SYM_FN.
+static LLVMTypeRef func_full_type(struct symbol *type)
+{
+	if (type->type == SYM_NODE) {
+		struct symbol *btype = type->ctype.base_type;
+		if (btype->type == SYM_PTR)
+			type = btype->ctype.base_type;
+	}
+	return symbol_type(type);
+}
+#endif
+
 static LLVMTypeRef sym_func_type(struct symbol *sym)
 {
 	int n_arg = symbol_list_size(sym->arguments);
@@ -178,7 +194,7 @@
 
 	/* don't cache the result for SYM_NODE */
 	if (sym->type == SYM_NODE)
-		return symbol_type(sym->ctype.base_type);
+		sym = sym->ctype.base_type;
 
 	if (sym->aux)
 		return sym->aux;
@@ -294,15 +310,20 @@
 		switch (expr->type) {
 		case EXPR_STRING: {
 			const char *s = expr->string->data;
+			size_t len = expr->string->length;
 			LLVMValueRef indices[] = { LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt64Type(), 0, 0) };
 			LLVMValueRef data;
 
-			data = LLVMAddGlobal(module, LLVMArrayType(LLVMInt8Type(), strlen(s) + 1), ".str");
+			data = LLVMAddGlobal(module, LLVMArrayType(LLVMInt8Type(), len), ".str");
 			LLVMSetLinkage(data, LLVMPrivateLinkage);
 			LLVMSetGlobalConstant(data, 1);
-			LLVMSetInitializer(data, LLVMConstString(strdup(s), strlen(s) + 1, true));
+			LLVMSetInitializer(data, LLVMConstString(s, len, true));
 
+#if LLVM_VERSION_MAJOR > 14
+			result = LLVMConstGEP2(LLVMTypeOf(data), data, indices, ARRAY_SIZE(indices));
+#else
 			result = LLVMConstGEP(data, indices, ARRAY_SIZE(indices));
+#endif
 			return result;
 		}
 		default:
@@ -485,7 +506,11 @@
 	/* convert base to char* type */
 	base = LLVMBuildPointerCast(builder, base, bytep, name);
 	/* addr = base + off */
+#if LLVM_VERSION_MAJOR > 14
+	addr = LLVMBuildInBoundsGEP2(builder, LLVMTypeOf(base),  base, &off, 1, name);
+#else
 	addr = LLVMBuildInBoundsGEP(builder, base, &off, 1, name);
+#endif
 	/* convert back to the actual pointer type */
 	addr = LLVMBuildPointerCast(builder, addr, type, name);
 	return addr;
@@ -711,7 +736,11 @@
 
 	/* perform load */
 	pseudo_name(insn->target, name);
+#if LLVM_VERSION_MAJOR > 14
+	target = LLVMBuildLoad2(fn->builder, symbol_type(insn->type), addr, name);
+#else
 	target = LLVMBuildLoad(fn->builder, addr, name);
+#endif
 
 	insn->target->priv = target;
 }
@@ -797,6 +826,7 @@
 static void output_op_call(struct function *fn, struct instruction *insn)
 {
 	LLVMValueRef target, func;
+	struct symbol *fntype;
 	struct symbol *ctype;
 	int n_arg = 0, i;
 	struct pseudo *arg;
@@ -812,14 +842,20 @@
 	else
 		func = pseudo_to_value(fn, ctype, insn->func);
 	i = 0;
+	fntype = ctype;			// first symbol in the list is the function 'true' type
 	FOR_EACH_PTR(insn->arguments, arg) {
-		NEXT_PTR_LIST(ctype);
+		NEXT_PTR_LIST(ctype);	// the remaining ones are the arguments' type
 		args[i++] = pseudo_to_rvalue(fn, ctype, arg);
 	} END_FOR_EACH_PTR(arg);
 	FINISH_PTR_LIST(ctype);
 
 	pseudo_name(insn->target, name);
+#if LLVM_VERSION_MAJOR > 14
+	target = LLVMBuildCall2(fn->builder, func_full_type(fntype), func, args, n_arg, name);
+#else
+	(void) fntype;
 	target = LLVMBuildCall(fn->builder, func, args, n_arg, name);
+#endif
 
 	insn->target->priv = target;
 }
@@ -1212,8 +1248,9 @@
 		}
 		case EXPR_STRING: {
 			const char *s = initializer->string->data;
+			size_t len = initializer->string->length;
 
-			initial_value = LLVMConstString(strdup(s), strlen(s) + 1, true);
+			initial_value = LLVMConstString(s, len, true);
 			break;
 		}
 		default:
diff --git a/target-xtensa.c b/target-xtensa.c
index 3620b4a..48e4bf7 100644
--- a/target-xtensa.c
+++ b/target-xtensa.c
@@ -22,7 +22,7 @@
 const struct target target_xtensa = {
 	.mach = MACH_XTENSA,
 	.bitness = ARCH_LP32,
-	.big_endian = true,
+	.big_endian = false,
 
 	.bits_in_longdouble = 64,
 
diff --git a/validation/backend/call-variadic.c b/validation/backend/call-variadic.c
index 4924e3f..f6f3fe0 100644
--- a/validation/backend/call-variadic.c
+++ b/validation/backend/call-variadic.c
@@ -11,17 +11,9 @@
 /*
  * check-name: call-variadic
  * check-command: sparse-llvm-dis -m64 $file
+ * check-output-ignore
+ * check-output-contains: , ...) @print(\\(i8\\*\\|ptr\\) %ARG1., i32 120, i32 %ARG2., i32 8, i64 %ARG3., i64 0, \\(i32\\*\\|ptr\\) %ARG4., \\(i8\\*\\|ptr\\) null)
+ * check-output-contains: define i32 @foo(
+ * check-output-contains: declare i32 @print(
  *
- * check-output-start
-; ModuleID = '<stdin>'
-source_filename = "sparse"
-
-define i32 @foo(i8* %ARG1., i32 %ARG2., i64 %ARG3., i32* %ARG4.) {
-L0:
-  %R5. = call i32 (i8*, ...) @print(i8* %ARG1., i32 120, i32 %ARG2., i32 8, i64 %ARG3., i64 0, i32* %ARG4., i8* null)
-  ret i32 %R5.
-}
-
-declare i32 @print(i8*, ...)
- * check-output-end
  */
diff --git a/validation/backend/cast.c b/validation/backend/cast.c
index f412240..9dfa6da 100644
--- a/validation/backend/cast.c
+++ b/validation/backend/cast.c
@@ -6,10 +6,12 @@
 typedef unsigned long ulong;
 typedef long long longlong;
 typedef unsigned long long ulonglong;
+typedef void *vptr;
+typedef int *iptr;
 
 #define DEFINE_CAST(from, to)			\
 	static to from##2##to(from x) {		\
-		return x;			\
+		return (to)x;			\
 	}
 
 #define DEFINE_CASTS(from)			\
@@ -43,6 +45,13 @@
 DEFINE_CASTS(float)
 DEFINE_CASTS(double)
 
+DEFINE_CAST(long, vptr)
+DEFINE_CAST(long, iptr)
+DEFINE_CAST(vptr, long)
+DEFINE_CAST(iptr, long)
+DEFINE_CAST(int, vptr)
+DEFINE_CAST(vptr, int)
+
 /*
  * check-name: Cast code generation
  * check-command: sparsec -c $file -o tmp.o
diff --git a/validation/linear/call-inline.c b/validation/linear/call-inline.c
index 1ad785e..0281e2d 100644
--- a/validation/linear/call-inline.c
+++ b/validation/linear/call-inline.c
@@ -13,6 +13,6 @@
  *
  * check-output-ignore
  * check-output-excludes: load
- * check-output-excludes: \\tcall
+ * check-output-excludes: ^.call
  * check-output-pattern(5): ret\\..* \\$42
  */
diff --git a/validation/reassoc/bug-zephyr-63417.c b/validation/reassoc/bug-zephyr-63417.c
new file mode 100644
index 0000000..5db821d
--- /dev/null
+++ b/validation/reassoc/bug-zephyr-63417.c
@@ -0,0 +1,13 @@
+extern char __tdata_size[];
+extern char __tdata_align[];
+
+unsigned long z_tls_data_size(void);
+unsigned long z_tls_data_size(void)
+{
+	return ((unsigned long)__tdata_size) + ((unsigned long)__tdata_align - 1);
+}
+
+/*
+ * check-name: bug-zephyr-63417
+ * check-timeout:
+ */