Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xcvsimd and Xcvbitmanip tests generator script #3

Open
melonedo opened this issue Sep 16, 2023 · 0 comments
Open

Xcvsimd and Xcvbitmanip tests generator script #3

melonedo opened this issue Sep 16, 2023 · 0 comments

Comments

@melonedo
Copy link
Owner

melonedo commented Sep 16, 2023

This is a Pluto notebook, you will need to save it as a local file, then run Pluto.jl and open it in the browser.

### A Pluto.jl notebook ###
# v0.19.26

using Markdown
using InteractiveUtils

# ╔═╡ e4089c6e-90ea-11ed-1505-6957a4684c28
using XLSX, Underscores,Dictionaries

# ╔═╡ 795eab7d-eea4-400c-af7d-805822ea7257
const f = XLSX.readxlsx("d:\\Downloads\\pulp_encoding_blocks-OPHW-2022-11-14.xlsx") # input

# ╔═╡ 543ad4dd-6db7-408c-bfdd-fead2d4e1565
const folder = raw"D:\Code\pluto\corev-test" # output

# ╔═╡ 57d3c765-39ea-42ec-a781-81198cb6198a
const sh = f["custom-3"]

# ╔═╡ f18ba16a-b870-4c10-b265-34bbd03352be
const row_iter = @_ XLSX.eachtablerow(sh; column_labels=split("funct5 F b25 rs2 rs1 funct3 rd opcode mnemonic")) |> Iterators.drop(__, 1) |> Iterators.filter(_[:mnemonic] != "-", __)

# ╔═╡ fc39e3f5-6fbe-4e1d-b32d-dcd718dc318c
first(row_iter)[:mnemonic]

# ╔═╡ 3b4f7723-6b1d-40ab-bf81-46d474872d3f
remove_space(s) = replace(s, " "=>"")

# ╔═╡ 9c83e2cf-0637-445c-8ead-9d6f6f5c935a
parse_bits(x) = parse(UInt32, remove_space(x); base=2)

# ╔═╡ 9d5f5f79-4c4f-44e4-8055-993f90284270
parse_bits(first(row_iter)[:opcode])

# ╔═╡ 7007f685-4693-47d5-acdf-195fed441cf8
begin
	mutable struct Bits
		x::UInt32
	end
	Bits() = Bits(0)
end

# ╔═╡ c01df47f-98a6-4cae-97f4-956833fbcb04
function Base.setindex!(b::Bits, v::Integer, r::Union{Integer,AbstractRange})
	if r isa Integer
		r = r:r
	end
	v &= 1<<length(r) - 1
	b.x |= v << minimum(r)
end

# ╔═╡ e20e5652-e720-419c-bc59-999d79302166
Base.getindex(b::Bits, r::AbstractRange)::UInt32 = (b.x >> minimum(r)) & (1<<length(r) - 1)

# ╔═╡ 909a4a78-69f9-4b85-9f4f-67237e14e874
Base.getindex(b::Bits, r::Integer) = b[r:r]

# ╔═╡ f1fc2128-ee6d-42dd-b3e8-1165a87a1926
let
	op = Bits()
	op[0:6] = parse(Int, "1111011"; base=2)
	op[2]
end

# ╔═╡ 756f3f7e-8a50-445d-8719-7e4c1b26064d
hex(x) = "0x" * bytes2hex(x)

# ╔═╡ 48832351-330c-4ae7-85fc-92e11875605a
const cases = [
	((5, 6, 7, 0), ("rD"=>"t0", "rs1"=>"t1", "rs2"=>"t2", "Imm6"=>"0"))
	((28, 29, 30, 32), ("rD"=>"t3", "rs1"=>"t4", "rs2"=>"t5", "Imm6"=>"32"))
	((10, 11, 12, 7), ("rD"=>"a0", "rs1"=>"a1", "rs2"=>"a2", "Imm6"=>"7"))
	((8, 9, 18, 63), ("rD"=>"s0", "rs1"=>"s1", "rs2"=>"s2", "Imm6"=>"63"))
]

# ╔═╡ d9984a6f-cde8-4926-aa3b-ced49bc5490d
let
	mkdir(folder)
	mkdir(joinpath(folder, "simd"))
	mkdir(joinpath(folder, "corev-intrinsics"))
	mkdir(joinpath(folder, "bitmanip"))
end

# ╔═╡ 03bd8cd5-0d3c-47ca-bdf6-c082db066ec6
instruction_name(r) = match(r"([\w.]+)\s+(.+)", r[:mnemonic])[1]

# ╔═╡ afad40dc-fbe6-454c-89d5-07505cea7803
function classify(r, name)
	if ismissing(r[:rs2])
		@assert r[:b25] == "Imm6[0|5:1]"
		if occursin(r"minu|maxu|cmp[gl][et]u|s?dotup|shuffle(?:[iI][0123])?|and|x?or|insert|extractu?|sr[la]|sll", name)
			:ru
		else
			:ri
		end
	elseif r[:rs2] == "src2"
		:rr
	else
		@assert r[:rs2] == "00000"
		:r
	end
end

# ╔═╡ 815ecd42-6fd3-4e9f-ab06-4ee3cdda83b9
open(joinpath(folder, "all.s"), "w+") do io_all
	# io = IOBuffer()
	# io_invalid = IOBuffer()
	println(io_all, """
	# RUN: llvm-mc -triple=riscv32 --mattr=+xcorevsimd -show-encoding %s 2>&1 \\
	# RUN:        | FileCheck %s --check-prefixes=CHECK-ENCODING,CHECK-INSTR
	# RUN: llvm-mc -filetype=obj -triple=riscv32 --mattr=+xcorevsimd < %s \\
	# RUN:     | llvm-objdump -M no-aliases  --mattr=+xcorevsimd -d -r - \\
	# RUN:     | FileCheck -check-prefixes=CHECK-INSTR %s
	""")
	for r in row_iter
		name = instruction_name(r)
		t = classify(r, name)

		file = joinpath(folder, "simd-old", replace(name, r"^cv|\."=>"") * ".s")
		open(file, "w+") do io
		println(io, """
		# RUN: llvm-mc -triple=riscv32 --mattr=+xcorevsimd -show-encoding %s \\
		# RUN:        | FileCheck %s --check-prefixes=CHECK-ENCODING,CHECK-INSTR
		""")
			for case in cases
				rd, rs1, rs2, imm6 = case[1]
				mnm = replace(r[:mnemonic], case[2]...)
				# Make ru behave like ri
				t = t == :ru ? :ri : t
				if t == :ri
					mnm = replace(mnm, "32"=>"-32", "63"=>"-1")
				end
	
				op = Bits()
				op[0:6] = r[:opcode] |> parse_bits
				@assert r[:rd] == "dest"
				op[7:11] = rd
				op[12:14] = r[:funct3] |> parse_bits
				@assert r[:rs1] == "src1"
				op[15:19] = rs1
				if t == :ri || t == :ru
					op[20:24] = Bits(imm6)[1:5]
					op[25] = Bits(imm6)[0]
				elseif t == :rr
					op[20:24] = rs2
					op[25] = r[:b25]
				else
					@assert t == :r
					op[20:24] == 0
					op[25] = r[:b25]
				end
				op[26] = r[:F] |> parse_bits
				op[27:31] = r[:funct5] |> parse_bits
		
				bytes = reinterpret(UInt8, [op.x])
		
				println(io_all, """
				$mnm
				# CHECK-INSTR: $mnm
				# CHECK-ENCODING: [$(join(hex.(bytes), ','))] 
				""")
				println(io, """
				$mnm
				# CHECK-INSTR: $mnm
				# CHECK-ENCODING: [$(join(hex.(bytes), ','))]
				""")
			end
		end
		invalid_file = joinpath(folder, "simd-old", replace(name, r"^cv|\."=>"") * "-invalid.s")
		open(invalid_file, "w+") do io_invalid
			println(io_invalid, """
			# RUN: not llvm-mc -triple=riscv32 --mattr=+xcorevsimd %s 2>&1 \\
			# RUN:        | FileCheck %s --check-prefixes=CHECK-ERROR
	
			$name 0, t1, t2
			# CHECK-ERROR: invalid operand for instruction
	
			$name t0, 0, t2
			# CHECK-ERROR: invalid operand for instruction
			""")
			(t == :rr || t == :r) && println(io_invalid, """
			$name t0, t1, t2, t3
			# CHECK-ERROR: invalid operand for instruction
			""")
	
			t == :r && println(io_invalid, """
			$name t0, t1, t2
			# CHECK-ERROR: invalid operand for instruction
			""")

			t == :ri && println(io_invalid, """
			$name t0, t1, t2, t3
			# CHECK-ERROR: immediate must be an integer in the range [-32, 31]
			
			$name t0, t1, t2
			# CHECK-ERROR: immediate must be an integer in the range [-32, 31]

			$name t0, t1, 63
			# CHECK-ERROR: immediate must be an integer in the range [-32, 31]
			""")

			t == :ru && println(io_invalid, """
			$name t0, t1, t2, t3
			# CHECK-ERROR: immediate must be an integer in the range [0, 63]
			
			$name t0, t1, t2
			# CHECK-ERROR: immediate must be an integer in the range [0, 63]

			$name t0, t1, -1
			# CHECK-ERROR: immediate must be an integer in the range [0, 63]
			""")

			t == :ri || t == :ru ||  println(io_invalid, """
			$name t0, t1, 0
			# CHECK-ERROR: invalid operand for instruction
			""")
	
			t == :ri || t == :ru || println(io_invalid, """
			$name t0, t1, 0
			# CHECK-ERROR: invalid operand for instruction
			""")
	
			t == :r || println(io_invalid, """
			$name t0, t1
			# CHECK-ERROR: too few operands for instruction
			""")
		end
	end
	# io_invalid |> take! |> String |> Docs.Text
end

# ╔═╡ f72f15dc-e64d-4d32-b0b5-c6271b11ce88
function write_ll_test(io, name, t, inst_name=name, test_name="test."*name)
	if t == :rr
		fnargs = "i32 %a, i32 %b"
	else
		fnargs = "i32 %a"
	end
	if t == :r
		instargs = "a0, a0"
	elseif t == :rr
		instargs = "a0, a0, a1"
	else
		instargs = "a0, a0, 5"
	end
	if t in (:ri, :ru)
		immargs = ", i32 5"
	else
		immargs = ""
	end
	println(io, """
	define i32 @$test_name($fnargs) {
	; CHECK-LABEL: $test_name:
	; CHECK:       # %bb.0:
	; CHECK-NEXT:    $inst_name $instargs
	; CHECK-NEXT:    ret
	  %1 = call i32 @llvm.riscv.$name($fnargs$immargs)
	  ret i32 %1
	}
	""")
end

# ╔═╡ dc09c1d8-aaad-4262-b0a7-482fa0dbcbc8
instruction_set = dictionary((instruction_name(r)=>classify(r, instruction_name(r))) for r in row_iter)

# ╔═╡ 44e29284-6424-4c1b-998f-9e5c8a10995c
const instruction_set_sc = @_ filter(
	!occursin(".sci.", _) || 
	!(replace(_, ".sci."=>".sc.") in keys(instruction_set)), keys(instruction_set)) |> getindices(instruction_set, __) # note: all .sc have .sci counterpart

# ╔═╡ 27a58aa1-a92c-4f7f-86ff-40789f8e04a5
const instruction_set_div = @_ filter(
	!occursin(r"\.div\d|shuffleI[123]", _), keys(instruction_set_sc)) |> getindices(instruction_set_sc, __)

# ╔═╡ 530a37f0-545d-4536-b0f0-a7250ad97385
open(joinpath(folder, "codegen.ll"), "w+") do io
	println(io, """
	; RUN: llc -O0 -mtriple=riscv32 -mattr=+m -mattr=+xcorevsimd -verify-machineinstrs < %s \\
	; RUN:   | FileCheck %s
	""")
	for (name, t) in pairs(instruction_set_sc)
		if t == :r
			println(io, "declare i32 @llvm.riscv.$name(i32)")
		else
			println(io, "declare i32 @llvm.riscv.$name(i32, i32)")
		end
		println(io)
		write_ll_test(io, name, t)
		if occursin(".sc.", name)
			sci_name = replace(name, ".sc."=>".sci.")
			write_ll_test(io, name, :ri, sci_name, "test."*sci_name)
		end
	end
end

# ╔═╡ ca7d0256-d037-4a55-a269-f6c03c9077b4
function builtin_format(name)
	# is cv.cplxconj unary or binary?
	if occursin(r"abs|cplxconj", name)
		"UZiUZi" # u32(u32)
	elseif occursin(r"extractu", name)
		"UZiUZiUc" # u32(u32,u8)
	elseif occursin(r"extract", name)
		"ZiUZiUc" # i32(u32,u8)
	elseif occursin(r"insert", name)
		"UZiUZiUZiIUc" # u32(u32,u32,const u8)
	elseif occursin(r"shuffle(:?I\d)?_sci", name)
		"UZiUZiUc" # u32(u32,u8)
	elseif occursin(r"pack(?:hi|lo)|shuffle2", name)
		"UZiUZiUZiUZi" # u32(u32,u32,u32)
	elseif occursin(r"add.h|sub.h|subrotmj", name)
		"UZiUZiUZiIUc" # u32(u32,u32,const u8)
	elseif occursin(r"cplxmul", name)
		"UZiUZiUZiUZiIUc" # u32(u32,u32,u32,const u8)
	elseif occursin(r"s?dot", name)
		ret = occursin(r"up", name) ? "UZi" : "Zi"
		if occursin(r"usp", name)
			args = "UZiSZi"
		elseif occursin(r"up", name)
			args = "UZiUZi"
		else
			@assert occursin(r"sp", name)
			args = "SZiSZi"
		end
		if occursin(".sc.b", name)
			args = args[begin:end-2] * 'c'
		elseif occursin(".sc.h", name)
			args = args[begin:end-2] * 's'
		end
		if startswith(name, r"cv\.s")
			ret * args * ret
		else
			ret * args
		end
	# default
	elseif occursin(".sc.b", name)
		"UZiUZiSc"
	elseif occursin(".sc.h", name)
		"UZiUZiSs"
	else
		"UZiUZiUZi"
	end
end

# ╔═╡ 6a0aea53-d17d-464c-82f1-75726496b3a4
open(joinpath(folder, "builtins.def"), "w+") do io
	println(io, """
	//==- BuiltinsRISCVCOREV.def - RISC-V CORE-V Builtin database ----*- C++ -*-==//
	//
	// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
	// See https://llvm.org/LICENSE.txt for license information.
	// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
	//
	//===----------------------------------------------------------------------===//
	//
	// This file defines the CORE-V-specific builtin function database.  Users of
	// this file must define the BUILTIN macro to make use of this information.
	//
	//===----------------------------------------------------------------------===//
	
	#if defined(BUILTIN) && !defined(TARGET_BUILTIN)
	#   define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) BUILTIN(ID, TYPE, ATTRS)
	#endif
	""")
	# #if defined(TARGET_BUILTIN) && !defined(TARGET_DATA_ONLY_BUILTIN)
	# #   define TARGET_DATA_ONLY_BUILTIN(ID, TYPE, ATTRS, FEATURE)                  \\
	#   TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE)
	# #endif
	# """)
	for (name, t) in pairs(instruction_set_div)
		name_ = replace(name, r"^cv\.|I0"=>"", '.'=>'_')
		format = builtin_format(name)
		# Make them all u32 for now
		# format32 = replace(format, r"[cs]$"=>"Zi")
		attr = occursin('*', format) ? "n" : "nc"
		if occursin('*', format)
			dataonly = "DATA_ONLY_"
		else
			dataonly = ""
		end
		print(io, """
		TARGET_$(dataonly)BUILTIN(simd_$name_, "$format", "$attr", "xcvsimd")
		""")
	end
	print(io, """
	
	#undef BUILTIN
	#undef TARGET_BUILTIN
	#undef TARGET_DATA_ONLY_BUILTIN
	""")
end

# ╔═╡ e782d998-cec7-4328-9657-cde90359e1ea
function write_clang_test(io, format, name_, t, test_name="test_"*name_)
	# params = t in (:r, :ri) ? "uint32_t a" : "uint32_t a, uint32_t b"
	if t in (:r, :ri, :ru)
		params = "uint32_t a"
	else
		param_type = if occursin("_sc_b", name_)
			"uint8_t"
		elseif occursin("_sc_h", name_)
			"uint16_t"
		else
			"uint32_t"
		end
		params = t == :rrr ? 
			"uint32_t a, uint32_t b, uint32_t c" : 
			"uint32_t a, $param_type b"
	end
	
	if t == :r
		args = "a"
	elseif t == :rr
		args = "a, b"
	elseif t == :rrr
		args = "a, b, c"
	elseif t in (:ri, :ru)
		args = "a, 5"
	elseif t in (:rir, :rur)
		args = "a, 5, b"
	end
	println(io, """
	uint32_t $test_name($params) {
		return __builtin_riscv_cv_simd_$name_($args);
	}
	""")
	if t in (:ri, :rir)
		if t == :rir
			b = ", b"
		else
			b = ""
		end
		println(io, """
		uint32_t $(test_name)_negative($params) {
			return __builtin_riscv_cv_simd_$name_(a, -32$b);
		}
		""")
	end
end

# ╔═╡ 7c00d649-b996-46c1-98e0-dfeea8f3d895
open(joinpath(folder, "corev-intrinsics", "all.c"), "w+") do io	
	println(io, """
	// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
	// RUN: %clang_cc1 -triple riscv32 -target-feature +xcvsimd -emit-llvm %s -o - \\
	// RUN:     | FileCheck %s

	#include <stdint.h>
	""")
	for (name_dot, t) in pairs(instruction_set_div)
		f = builtin_format(name_dot)
		if occursin(r"pack(?:hi|lo)|insert", name_dot)
			continue
		end
		name_ = replace(name_dot, r"^cv\."=>"", '.'=>'_')
		if occursin(r"^(?:add|sub)_h|subrotmj", name_)
			for div in 0:3
				println(io, """
				uint32_t test_$(name_)_div$(2^div)(uint32_t a, uint32_t b) {
					return __builtin_riscv_cv_simd_$name_(a, b, $div);
				}
				""")
			end
		elseif occursin(r"cplxmul_[ri]$", name_)
			for div in 0:3
				println(io, """
				uint32_t test_$(name_)_div$(2^div)(uint32_t a, uint32_t b, uint32_t c) {
					return __builtin_riscv_cv_simd_$name_(a, b, c, $div);
				}
				""")
			end
		elseif occursin(r"I0", name_)
			for i in 0:3
				namei = replace(name_, "0"=>string(i))
				println(io, """
				uint32_t test_$namei(uint32_t a, uint32_t b, uint32_t c) {
					return __builtin_riscv_cv_simd_shuffle_sci_b(a, $i);
				}
				""")
			end
		else
			if occursin(r"sdot|shuffle2", name_)
				if t == :rr
					t = :rrr
				end
			end
			write_clang_test(io, f, name_, t)
			if occursin("_sc_", name_)
				name_sci = replace(name_, "_sc_"=>"_sci_")
				unsigned = occursin(r"minu|maxu|srl|sra|sll|cmp[gl][et]u|s?dotup", name_)
				t2 = if t == :rr
					unsigned ? :ru : :ri
				else
					@assert t == :rrr
					unsigned ? :rur : :rir
				end
				write_clang_test(io, f, name_, t2, "test_"*name_sci)
			end
		end
	end
end

# ╔═╡ 123f71e9-e3a4-4b10-84e3-6e241014ebdb
open(joinpath(folder, "all.s"), "w+") do io_all
	rm(joinpath(folder, "simd"); recursive=true)
	mkdir(joinpath(folder, "simd"))
	println(io_all, """
	# RUN: llvm-mc -triple=riscv32 --mattr=+xcvsimd -show-encoding %s 2>&1 \\
	# RUN:        | FileCheck %s --check-prefixes=CHECK-ENCODING,CHECK-INSTR
	# RUN: not llvm-mc -triple=riscv32 -show-encoding %s 2>&1 \\
	# RUN:        | FileCheck %s --check-prefix=CHECK-ERROR
	# RUN: llvm-mc -filetype=obj -triple=riscv32 --mattr=+xcvsimd < %s \\
	# RUN:     | llvm-objdump -M no-aliases  --mattr=+xcvsimd -d -r - \\
	# RUN:     | FileCheck -check-prefixes=CHECK-INSTR %s
	# RUN: llvm-mc -filetype=obj -triple=riscv32 --mattr=+xcvsimd %s \\
	# RUN:        | llvm-objdump -d - | FileCheck %s --check-prefix=CHECK-UNKNOWN
	""")
	for r in row_iter
		name = instruction_name(r)
		t = classify(r, name)

		file = joinpath(folder, "simd", replace(name, 
			r"^cv|\.(?:[bh]|sci?|div\d)?"=>"", 
			r"shuffleI\d"=>"shuffleIx",
			r"hi|lo" => "hilo",
		) * ".s")
		if !isfile(file)
			open(file, "w+") do io
				println(io, """
				# RUN: llvm-mc -triple=riscv32 --mattr=+xcvsimd -show-encoding %s \\
				# RUN:        | FileCheck %s --check-prefixes=CHECK-ENCODING,CHECK-INSTR
				""")
			end
		end
		open(file, "a") do io
			println(io, """
			//===----------------------------------------------------------------------===//
			// $name
			//===----------------------------------------------------------------------===//
			""")
			for case in cases
				rd, rs1, rs2, imm6 = case[1]
				mnm = replace(r[:mnemonic], case[2]...) |> lowercase
				if t == :ri
					mnm = replace(mnm, "32"=>"-32", "63"=>"-1")
				end
	
				op = Bits()
				op[0:6] = r[:opcode] |> parse_bits
				@assert r[:rd] == "dest"
				op[7:11] = rd
				op[12:14] = r[:funct3] |> parse_bits
				@assert r[:rs1] == "src1"
				op[15:19] = rs1
				if t == :ri || t == :ru
					op[20:24] = Bits(imm6)[1:5]
					op[25] = Bits(imm6)[0]
				elseif t == :rr
					op[20:24] = rs2
					op[25] = r[:b25]
				else
					@assert t == :r
					op[20:24] == 0
					op[25] = r[:b25]
				end
				op[26] = r[:F] |> parse_bits
				op[27:31] = r[:funct5] |> parse_bits
		
				bytes = reinterpret(UInt8, [op.x])
		
				println(io_all, """
				$mnm
				# CHECK-INSTR: $mnm
				# CHECK-ENCODING: [$(join(hex.(bytes), ','))] 
				# CHECK-ERROR: error: instruction requires the following: 'Xcvsimd' (SIMD ALU)
				# CHECK-UNKNOWN: $(join(bytes2hex.(bytes), ' ')) <unknown>
				""")
				println(io, """
				$mnm
				# CHECK-INSTR: $mnm
				# CHECK-ENCODING: [$(join(hex.(bytes), ','))]
				""")
			end
		end
		file_invalid = replace(file, r"\.s$"=>"-invalid.s")
		if !isfile(file_invalid)
			open(file_invalid, "w+") do io_invalid
				println(io_invalid, """
				# RUN: not llvm-mc -triple=riscv32 --mattr=+xcvsimd %s 2>&1 \\
				# RUN:        | FileCheck %s --check-prefixes=CHECK-ERROR
				""")
			end
		end
		open(file_invalid, "a") do io_invalid
			println(io_invalid, """
			//===----------------------------------------------------------------------===//
			// $name
			//===----------------------------------------------------------------------===//
			""")
			println(io_invalid, """
			$name 0, t1, t2
			# CHECK-ERROR: invalid operand for instruction

			$name t0, 0, t2
			# CHECK-ERROR: invalid operand for instruction
			""")
			(t == :rr || t == :r) && println(io_invalid, """
			$name t0, t1, t2, t3
			# CHECK-ERROR: invalid operand for instruction
			""")

			t == :r && println(io_invalid, """
			$name t0, t1, t2
			# CHECK-ERROR: invalid operand for instruction
			""")

			t == :ri && println(io_invalid, """
			$name t0, t1, t2, t3
			# CHECK-ERROR: immediate must be an integer in the range [-32, 31]

			$name t0, t1, t2
			# CHECK-ERROR: immediate must be an integer in the range [-32, 31]

			$name t0, t1, 63
			# CHECK-ERROR: immediate must be an integer in the range [-32, 31]
			""")

			t == :ru && println(io_invalid, """
			$name t0, t1, t2, t3
			# CHECK-ERROR: immediate must be an integer in the range [0, 63]

			$name t0, t1, t2
			# CHECK-ERROR: immediate must be an integer in the range [0, 63]

			$name t0, t1, -1
			# CHECK-ERROR: immediate must be an integer in the range [0, 63]

			$name t0, t1, 64
			# CHECK-ERROR: immediate must be an integer in the range [0, 63]
			""")

			t == :ri || t == :ru ||  println(io_invalid, """
			$name t0, t1, 0
			# CHECK-ERROR: invalid operand for instruction
			""")
	
			t == :ri || t == :ru || println(io_invalid, """
			$name t0, t1, 0
			# CHECK-ERROR: invalid operand for instruction
			""")
	
			t == :r || println(io_invalid, """
			$name t0, t1
			# CHECK-ERROR: too few operands for instruction
			""")
		end
	end
end

# ╔═╡ f2510dd4-a931-4c67-8884-7f4176d00ee0
const bitmanip_cases = [
	((5, 6, 7, 0, 1), ("rD"=>"t0", "rs1"=>"t1", "rs2"=>"t2"))
	((10, 11, 12, 17, 18), ("rD"=>"a0", "rs1"=>"a1", "rs2"=>"a2"))
	((8, 9, 18, 30, 31), ("rD"=>"s0", "rs1"=>"s1", "rs2"=>"s2"))
]

# ╔═╡ e9b31353-e152-4f28-bf6a-589bb5546259
const bitmanip_rii = @_ XLSX.eachtablerow(f["custom-2"]; column_labels=split("funct2 ls3 rs2 rs1 funct3 rd opcode mnemonic")) |> Iterators.drop(__, 1) |> Iterators.filter(_[:mnemonic] != "-", __) |> Iterators.take(__, 6)

# ╔═╡ 12dcf39b-e94e-4d41-88c0-4081ed58d760
const bitmanip_rr = @_ XLSX.eachtablerow(f["Plane A"]; column_labels=split("funct7 rs2 rs1 funct3 rd opcode mnemonic")) |> Iterators.drop(__, 25) |> Iterators.filter(_[:mnemonic] != "-", __) |> Iterators.take(__, 10)

# ╔═╡ 8be1690d-9ac1-442f-985a-bbac38b2d2a4
const bitmap_instructions = Iterators.flatten((zip(Iterators.repeated(:rii), bitmanip_rii), zip([:rr, :rr, :rr, :rr, :rr, :rr, :r, :r, :r, :r], bitmanip_rr)))

# ╔═╡ 1cb58895-42d0-4887-9f6c-9adc4a759b5a
let
	rm(joinpath(folder, "bitmanip"); recursive=true)
	mkdir(joinpath(folder, "bitmanip"))

	first = true
	first_invalid = true
	for (t, r) in bitmap_instructions
		name = instruction_name(r)

		file = joinpath(folder, "bitmanip", name[4:end] * ".s")
		# all in one file
		file = joinpath(folder, "bitmanip", "XCVbitmanip.s")
		open(file, "a") do io
			first && println(io, """
				# RUN: llvm-mc -triple=riscv32 --mattr=+xcvbitmanip -show-encoding %s \\
				# RUN:        | FileCheck %s --check-prefixes=CHECK-ENCODING,CHECK-INSTR
				#
				# RUN: not llvm-mc -triple riscv32 %s 2>&1 \\
				# RUN:     | FileCheck -check-prefix=CHECK-NO-EXT %s
				""")
			first = false
			for case in bitmanip_cases
				rd, rs1, rs2, is3, is2 = case[1]
				if name == "cv.bitrev"
					is3 %= 4
				end
				mnm = replace(r[:mnemonic], case[2]..., "Is3"=>string(is3), "Is2"=>string(is2))
	
				op = Bits()
				op[0:6] = r[:opcode] |> parse_bits
				@assert r[:rd] == "dest"
				op[7:11] = rd
				op[12:14] = r[:funct3] |> parse_bits
				@assert r[:rs1] in ("src", "src1")
				op[15:19] = rs1
				if t == :rii
					op[20:24] = is2
					op[25:29] = is3
					op[30:31] = r[:funct2] |> parse_bits
				else
					if t == :r
						op[20:24] = 0
					else
						@assert t == :rr
						@assert r[:rs2] == "src2"
						op[20:24] = rs2
					end
					op[25:31] = r[:funct7] |> parse_bits
				end
		
				bytes = reinterpret(UInt8, [op.x])
		
				println(io, """
				$mnm
				# CHECK-INSTR: $mnm
				# CHECK-ENCODING: [$(join(hex.(bytes), ','))]
				# CHECK-NO-EXT: instruction requires the following: 'XCVbitmanip' (Bit Manipulation){{\$}}
				""")
			end
		end
		file_invalid = replace(file, r"\.s$"=>"-invalid.s")
		open(file_invalid, "a") do io_invalid
			
			first_invalid && println(io_invalid, """
				# RUN: not llvm-mc -triple=riscv32 --mattr=+xcvbitmanip %s 2>&1 \\
				# RUN:        | FileCheck %s --check-prefixes=CHECK-ERROR
				""")
			first_invalid = false
			
			r = name == "cv.bitrev" ? 3 : 31
			t == :rii && println(io_invalid, """
			$name t0, t1
			# CHECK-ERROR: too few operands for instruction

			$name t0, t1, 0
			# CHECK-ERROR: too few operands for instruction

			$name t0, t1, t2
			# CHECK-ERROR: immediate must be an integer in the range [0, $r]

			$name t0, t1, t2, t3
			# CHECK-ERROR: immediate must be an integer in the range [0, $r]

			$name t0, t1, 0, 32
			# CHECK-ERROR: immediate must be an integer in the range [0, 31]

			$name t0, t1, 0, -1
			# CHECK-ERROR: immediate must be an integer in the range [0, 31]

			$name t0, t1, 32, 0
			# CHECK-ERROR: immediate must be an integer in the range [0, $r]

			$name t0, t1, -1, 0
			# CHECK-ERROR: immediate must be an integer in the range [0, $r]
			""")
			
			t in (:rr, :r) && println(io_invalid, """
			$name t0
			# CHECK-ERROR: too few operands for instruction

			$name t0, 0
			# CHECK-ERROR: invalid operand for instruction

			$name t0, t1, 0
			# CHECK-ERROR: invalid operand for instruction
			""")

			t == :rr && println(io_invalid, """
			$name t0, t1
			# CHECK-ERROR: too few operands for instruction
			""")

			t == :r && println(io_invalid, """
			$name t0, t1, t2
			# CHECK-ERROR: invalid operand for instruction 
			""")

		end
	end
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
Underscores = "d9a01c3f-67ce-4d8c-9b55-35f6e4050bb1"
XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0"

[compat]
Dictionaries = "~0.3.25"
Underscores = "~3.0.0"
XLSX = "~0.8.4"
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

julia_version = "1.9.1"
manifest_format = "2.0"
project_hash = "1feac6b77b557815d3cf8107e7875403c10cb661"

[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
version = "1.1.1"

[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[deps.CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "1.0.2+0"

[[deps.DataAPI]]
git-tree-sha1 = "e8119c1a33d267e16108be441a287a6981ba1630"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.14.0"

[[deps.DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"

[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[deps.Dictionaries]]
deps = ["Indexing", "Random", "Serialization"]
git-tree-sha1 = "e82c3c97b5b4ec111f3c1b55228cebc7510525a2"
uuid = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
version = "0.3.25"

[[deps.Downloads]]
deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
version = "1.6.0"

[[deps.EzXML]]
deps = ["Printf", "XML2_jll"]
git-tree-sha1 = "0fa3b52a04a4e210aeb1626def9c90df3ae65268"
uuid = "8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615"
version = "1.1.0"

[[deps.FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"

[[deps.Indexing]]
git-tree-sha1 = "ce1566720fd6b19ff3411404d4b977acd4814f9f"
uuid = "313cdc1a-70c2-5d6a-ae34-0150d3930a38"
version = "1.1.1"

[[deps.InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[deps.IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"

[[deps.JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.4.1"

[[deps.LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
version = "0.6.3"

[[deps.LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
version = "7.84.0+0"

[[deps.LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[deps.LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
version = "1.10.2+0"

[[deps.Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[deps.Libiconv_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c7cb1f5d892775ba13767a87c7ada0b980ea0a71"
uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
version = "1.16.1+2"

[[deps.LinearAlgebra]]
deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[deps.Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[deps.Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[deps.MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
version = "2.28.2+0"

[[deps.MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
version = "2022.10.11"

[[deps.NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
version = "1.2.0"

[[deps.OpenBLAS_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"
version = "0.3.21+4"

[[deps.OrderedCollections]]
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.1"

[[deps.Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
version = "1.9.0"

[[deps.Preferences]]
deps = ["TOML"]
git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.3.0"

[[deps.Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[deps.REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[deps.Random]]
deps = ["SHA", "Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[deps.SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
version = "0.7.0"

[[deps.Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[deps.Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[deps.TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
version = "1.0.3"

[[deps.TableTraits]]
deps = ["IteratorInterfaceExtensions"]
git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
version = "1.0.1"

[[deps.Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"]
git-tree-sha1 = "c79322d36826aa2f4fd8ecfa96ddb47b174ac78d"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.10.0"

[[deps.Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
version = "1.10.0"

[[deps.Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[deps.UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[deps.Underscores]]
git-tree-sha1 = "6e6de5a5e7116dcff8effc99f6f55230c61f6862"
uuid = "d9a01c3f-67ce-4d8c-9b55-35f6e4050bb1"
version = "3.0.0"

[[deps.Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

[[deps.XLSX]]
deps = ["Artifacts", "Dates", "EzXML", "Printf", "Tables", "ZipFile"]
git-tree-sha1 = "ccd1adf7d0b22f762e1058a8d73677e7bd2a7274"
uuid = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0"
version = "0.8.4"

[[deps.XML2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "93c41695bc1c08c46c5899f4fe06d6ead504bb73"
uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
version = "2.10.3+0"

[[deps.ZipFile]]
deps = ["Libdl", "Printf", "Zlib_jll"]
git-tree-sha1 = "f492b7fe1698e623024e873244f10d89c95c340a"
uuid = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"
version = "0.10.1"

[[deps.Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
version = "1.2.13+0"

[[deps.libblastrampoline_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
version = "5.8.0+0"

[[deps.nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
version = "1.48.0+0"

[[deps.p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
version = "17.4.0+0"
"""

# ╔═╡ Cell order:
# ╠═e4089c6e-90ea-11ed-1505-6957a4684c28
# ╠═795eab7d-eea4-400c-af7d-805822ea7257
# ╠═543ad4dd-6db7-408c-bfdd-fead2d4e1565
# ╠═57d3c765-39ea-42ec-a781-81198cb6198a
# ╠═f18ba16a-b870-4c10-b265-34bbd03352be
# ╠═fc39e3f5-6fbe-4e1d-b32d-dcd718dc318c
# ╠═3b4f7723-6b1d-40ab-bf81-46d474872d3f
# ╠═9c83e2cf-0637-445c-8ead-9d6f6f5c935a
# ╠═9d5f5f79-4c4f-44e4-8055-993f90284270
# ╠═7007f685-4693-47d5-acdf-195fed441cf8
# ╠═c01df47f-98a6-4cae-97f4-956833fbcb04
# ╠═e20e5652-e720-419c-bc59-999d79302166
# ╠═909a4a78-69f9-4b85-9f4f-67237e14e874
# ╠═f1fc2128-ee6d-42dd-b3e8-1165a87a1926
# ╠═756f3f7e-8a50-445d-8719-7e4c1b26064d
# ╠═48832351-330c-4ae7-85fc-92e11875605a
# ╠═d9984a6f-cde8-4926-aa3b-ced49bc5490d
# ╠═03bd8cd5-0d3c-47ca-bdf6-c082db066ec6
# ╠═afad40dc-fbe6-454c-89d5-07505cea7803
# ╠═815ecd42-6fd3-4e9f-ab06-4ee3cdda83b9
# ╠═f72f15dc-e64d-4d32-b0b5-c6271b11ce88
# ╠═dc09c1d8-aaad-4262-b0a7-482fa0dbcbc8
# ╠═44e29284-6424-4c1b-998f-9e5c8a10995c
# ╠═27a58aa1-a92c-4f7f-86ff-40789f8e04a5
# ╠═530a37f0-545d-4536-b0f0-a7250ad97385
# ╠═ca7d0256-d037-4a55-a269-f6c03c9077b4
# ╠═6a0aea53-d17d-464c-82f1-75726496b3a4
# ╠═e782d998-cec7-4328-9657-cde90359e1ea
# ╠═7c00d649-b996-46c1-98e0-dfeea8f3d895
# ╠═123f71e9-e3a4-4b10-84e3-6e241014ebdb
# ╠═f2510dd4-a931-4c67-8884-7f4176d00ee0
# ╠═e9b31353-e152-4f28-bf6a-589bb5546259
# ╠═12dcf39b-e94e-4d41-88c0-4081ed58d760
# ╠═8be1690d-9ac1-442f-985a-bbac38b2d2a4
# ╠═1cb58895-42d0-4887-9f6c-9adc4a759b5a
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002
melonedo pushed a commit that referenced this issue Sep 16, 2023
TSan reports the following data race:

  Write of size 4 at 0x000109e0b160 by thread T2 (mutexes: write M0, write M1):
    #0 NativeFile::Close() File.cpp:329
    #1 ConnectionFileDescriptor::Disconnect(lldb_private::Status*) ConnectionFileDescriptorPosix.cpp:232
    #2 Communication::Disconnect(lldb_private::Status*) Communication.cpp:61
    #3 process_gdb_remote::ProcessGDBRemote::DidExit() ProcessGDBRemote.cpp:1164
    openhwgroup#4 Process::SetExitStatus(int, char const*) Process.cpp:1097
    openhwgroup#5 process_gdb_remote::ProcessGDBRemote::MonitorDebugserverProcess(...) ProcessGDBRemote.cpp:3387

  Previous read of size 4 at 0x000109e0b160 by main thread (mutexes: write M2):
    #0 NativeFile::IsValid() const File.h:393
    #1 ConnectionFileDescriptor::IsConnected() const ConnectionFileDescriptorPosix.cpp:121
    #2 Communication::IsConnected() const Communication.cpp:79
    #3 process_gdb_remote::GDBRemoteCommunication::WaitForPacketNoLock(...) GDBRemoteCommunication.cpp:256
    openhwgroup#4 process_gdb_remote::GDBRemoteCommunication::WaitForPacketNoLock(...l) GDBRemoteCommunication.cpp:244
    openhwgroup#5 process_gdb_remote::GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(llvm::StringRef, StringExtractorGDBRemote&) GDBRemoteClientBase.cpp:246

The problem is that in WaitForPacketNoLock's run loop, it checks that
the connection is still connected. This races with the
ConnectionFileDescriptor disconnecting. Most (but not all) access to the
IOObject in ConnectionFileDescriptorPosix is already gated by the mutex.
This patch just protects IsConnected in the same way.

Differential revision: https://reviews.llvm.org/D157347
melonedo pushed a commit that referenced this issue Sep 16, 2023
TSan reports the following race:

  Write of size 8 at 0x000107707ee8 by main thread:
    #0 lldb_private::ThreadedCommunication::StartReadThread(...) ThreadedCommunication.cpp:175
    #1 lldb_private::Process::SetSTDIOFileDescriptor(...) Process.cpp:4533
    #2 lldb_private::Platform::DebugProcess(...) Platform.cpp:1121
    #3 lldb_private::PlatformDarwin::DebugProcess(...) PlatformDarwin.cpp:711
    openhwgroup#4 lldb_private::Target::Launch(...) Target.cpp:3235
    openhwgroup#5 CommandObjectProcessLaunch::DoExecute(...) CommandObjectProcess.cpp:256
    openhwgroup#6 lldb_private::CommandObjectParsed::Execute(...) CommandObject.cpp:751
    openhwgroup#7 lldb_private::CommandInterpreter::HandleCommand(...) CommandInterpreter.cpp:2054

  Previous read of size 8 at 0x000107707ee8 by thread T5:
    #0 lldb_private::HostThread::IsJoinable(...) const HostThread.cpp:30
    #1 lldb_private::ThreadedCommunication::StopReadThread(...) ThreadedCommunication.cpp:192
    #2 lldb_private::Process::ShouldBroadcastEvent(...) Process.cpp:3420
    #3 lldb_private::Process::HandlePrivateEvent(...) Process.cpp:3728
    openhwgroup#4 lldb_private::Process::RunPrivateStateThread(...) Process.cpp:3914
    openhwgroup#5 std::__1::__function::__func<lldb_private::Process::StartPrivateStateThread(...) function.h:356
    openhwgroup#6 lldb_private::HostNativeThreadBase::ThreadCreateTrampoline(...) HostNativeThreadBase.cpp:62
    openhwgroup#7 lldb_private::HostThreadMacOSX::ThreadCreateTrampoline(...) HostThreadMacOSX.mm:18

The problem is the lack of synchronization between starting and stopping
the read thread. This patch fixes that by protecting those operations
with a mutex.

Differential revision: https://reviews.llvm.org/D157361
melonedo pushed a commit that referenced this issue Sep 16, 2023
TSan reports the following data race:

  Write of size 4 at 0x000109e0b160 by thread T2 (...):
    #0 lldb_private::NativeFile::Close() File.cpp:329
    #1 lldb_private::ConnectionFileDescriptor::Disconnect(...) ConnectionFileDescriptorPosix.cpp:232
    #2 lldb_private::Communication::Disconnect(...) Communication.cpp:61
    #3 lldb_private::process_gdb_remote::ProcessGDBRemote::DidExit() ProcessGDBRemote.cpp:1164
    openhwgroup#4 lldb_private::Process::SetExitStatus(...) Process.cpp:1097
    openhwgroup#5 lldb_private::process_gdb_remote::ProcessGDBRemote::MonitorDebugserverProcess(...) ProcessGDBRemote.cpp:3387

  Previous read of size 4 at 0x000109e0b160 by main thread (...):
    #0 lldb_private::NativeFile::IsValid() const File.h:393
    #1 lldb_private::ConnectionFileDescriptor::IsConnected() const ConnectionFileDescriptorPosix.cpp:121
    #2 lldb_private::Communication::IsConnected() const Communication.cpp:79
    #3 lldb_private::process_gdb_remote::GDBRemoteCommunication::WaitForPacketNoLock(...) GDBRemoteCommunication.cpp:256
    openhwgroup#4 lldb_private::process_gdb_remote::GDBRemoteCommunication::WaitForPacketNoLock(...) GDBRemoteCommunication.cpp:244
    openhwgroup#5 lldb_private::process_gdb_remote::GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(...) GDBRemoteClientBase.cpp:246

I originally tried fixing the problem at the ConnectionFileDescriptor
level, but that operates on an IOObject which can have different thread
safety guarantees depending on its implementation.

For this particular issue, the problem is specific to NativeFile.
NativeFile can hold a file descriptor and/or a file stream. Throughout
its implementation, it checks if the descriptor or stream is valid and
do some operation on it if it is. While that works in a single threaded
environment, nothing prevents another thread from modifying the
descriptor or stream between the IsValid check and when it's actually
being used.

This patch prevents such issues by returning a ValueGuard RAII object.
As long as the object is in scope, the value is guaranteed by a lock.

Differential revision: https://reviews.llvm.org/D157347
melonedo pushed a commit that referenced this issue Sep 16, 2023
Thread sanitizer reports the following data race:

```
WARNING: ThreadSanitizer: data race (pid=43201)
  Write of size 4 at 0x00010520c474 by thread T1 (mutexes: write M0, write M1):
    #0 lldb_private::PipePosix::CloseWriteFileDescriptor() PipePosix.cpp:242 (liblldb.18.0.0git.dylib:arm64+0x414700) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
    #1 lldb_private::PipePosix::Close() PipePosix.cpp:217 (liblldb.18.0.0git.dylib:arm64+0x4144e8) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
    #2 lldb_private::ConnectionFileDescriptor::Disconnect(lldb_private::Status*) ConnectionFileDescriptorPosix.cpp:239 (liblldb.18.0.0git.dylib:arm64+0x40a620) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
    #3 lldb_private::Communication::Disconnect(lldb_private::Status*) Communication.cpp:61 (liblldb.18.0.0git.dylib:arm64+0x2a9318) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
    openhwgroup#4 lldb_private::process_gdb_remote::ProcessGDBRemote::DidExit() ProcessGDBRemote.cpp:1167 (liblldb.18.0.0git.dylib:arm64+0x8ed984) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)

  Previous read of size 4 at 0x00010520c474 by main thread (mutexes: write M2, write M3):
    #0 lldb_private::PipePosix::CanWrite() const PipePosix.cpp:229 (liblldb.18.0.0git.dylib:arm64+0x4145e4) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
    #1 lldb_private::ConnectionFileDescriptor::Disconnect(lldb_private::Status*) ConnectionFileDescriptorPosix.cpp:212 (liblldb.18.0.0git.dylib:arm64+0x40a4a8) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
    #2 lldb_private::Communication::Disconnect(lldb_private::Status*) Communication.cpp:61 (liblldb.18.0.0git.dylib:arm64+0x2a9318) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
    #3 lldb_private::process_gdb_remote::GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote&, lldb_private::Timeout<std::__1::ratio<1l, 1000000l>>, bool) GDBRemoteCommunication.cpp:373 (liblldb.18.0.0git.dylib:arm64+0x8b9c48) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
    openhwgroup#4 lldb_private::process_gdb_remote::GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote&, lldb_private::Timeout<std::__1::ratio<1l, 1000000l>>, bool) GDBRemoteCommunication.cpp:243 (liblldb.18.0.0git.dylib:arm64+0x8b9904) (BuildId: 2983976beb2637b5943bff32fd12eb8932000000200000000100000000000e00)
```

Fix this by adding a mutex to PipePosix.

Differential Revision: https://reviews.llvm.org/D157654
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant