transform: fix bug in StringToBytes optimization pass
Previously, this pass would convert any read-only use of a runtime.stringToBytes call to use the original string buffer instead. This is incorrect: if there are any writes to the resulting buffer, none of the slice buffer pointers can be converted to use the original read-only string buffer. This commit fixes that bug and adds a test to prove the new (correct) behavior.
Этот коммит содержится в:
		
							родитель
							
								
									081d2e58c6
								
							
						
					
					
						коммит
						a896f7f218
					
				
					 3 изменённых файлов: 44 добавлений и 6 удалений
				
			
		| 
						 | 
				
			
			@ -28,32 +28,44 @@ func OptimizeStringToBytes(mod llvm.Module) {
 | 
			
		|||
 | 
			
		||||
		// strptr is always constant because strings are always constant.
 | 
			
		||||
 | 
			
		||||
		convertedAllUses := true
 | 
			
		||||
		var pointerUses []llvm.Value
 | 
			
		||||
		canConvertPointer := true
 | 
			
		||||
		for _, use := range getUses(call) {
 | 
			
		||||
			if use.IsAExtractValueInst().IsNil() {
 | 
			
		||||
				// Expected an extractvalue, but this is something else.
 | 
			
		||||
				convertedAllUses = false
 | 
			
		||||
				canConvertPointer = false
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			switch use.Type().TypeKind() {
 | 
			
		||||
			case llvm.IntegerTypeKind:
 | 
			
		||||
				// A length (len or cap). Propagate the length value.
 | 
			
		||||
				// This can always be done because the byte slice is always the
 | 
			
		||||
				// same length as the original string.
 | 
			
		||||
				use.ReplaceAllUsesWith(strlen)
 | 
			
		||||
				use.EraseFromParentAsInstruction()
 | 
			
		||||
			case llvm.PointerTypeKind:
 | 
			
		||||
				// The string pointer itself.
 | 
			
		||||
				if !isReadOnly(use) {
 | 
			
		||||
					convertedAllUses = false
 | 
			
		||||
					// There is a store to the byte slice. This means that none
 | 
			
		||||
					// of the pointer uses can't be propagated.
 | 
			
		||||
					canConvertPointer = false
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				use.ReplaceAllUsesWith(strptr)
 | 
			
		||||
				use.EraseFromParentAsInstruction()
 | 
			
		||||
				// It may be that the pointer value can be propagated, if all of
 | 
			
		||||
				// the pointer uses are readonly.
 | 
			
		||||
				pointerUses = append(pointerUses, use)
 | 
			
		||||
			default:
 | 
			
		||||
				// should not happen
 | 
			
		||||
				panic("unknown return type of runtime.stringToBytes: " + use.Type().String())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if convertedAllUses {
 | 
			
		||||
		if canConvertPointer {
 | 
			
		||||
			// All pointer uses are readonly, so they can be converted.
 | 
			
		||||
			for _, use := range pointerUses {
 | 
			
		||||
				use.ReplaceAllUsesWith(strptr)
 | 
			
		||||
				use.EraseFromParentAsInstruction()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Call to runtime.stringToBytes can be eliminated: both the input
 | 
			
		||||
			// and the output is constant.
 | 
			
		||||
			call.EraseFromParentAsInstruction()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								transform/testdata/stringtobytes.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										16
									
								
								transform/testdata/stringtobytes.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
				
			
			@ -30,3 +30,19 @@ entry:
 | 
			
		|||
  call fastcc void @writeToSlice(ptr %1, i64 %2, i64 %3)
 | 
			
		||||
  ret void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
; Test that pointer values are never propagated if there is even a single write
 | 
			
		||||
; to the pointer value (but len/cap values still can be).
 | 
			
		||||
define void @testReadSome() {
 | 
			
		||||
entry:
 | 
			
		||||
  %s = call fastcc { ptr, i64, i64 } @runtime.stringToBytes(ptr @str, i64 6)
 | 
			
		||||
  %s.ptr = extractvalue { ptr, i64, i64 } %s, 0
 | 
			
		||||
  %s.len = extractvalue { ptr, i64, i64 } %s, 1
 | 
			
		||||
  %s.cap = extractvalue { ptr, i64, i64 } %s, 2
 | 
			
		||||
  call fastcc void @writeToSlice(ptr %s.ptr, i64 %s.len, i64 %s.cap)
 | 
			
		||||
  %s.ptr2 = extractvalue { ptr, i64, i64 } %s, 0
 | 
			
		||||
  %s.len2 = extractvalue { ptr, i64, i64 } %s, 1
 | 
			
		||||
  %s.cap2 = extractvalue { ptr, i64, i64 } %s, 2
 | 
			
		||||
  call fastcc void @printSlice(ptr %s.ptr2, i64 %s.len2, i64 %s.cap2)
 | 
			
		||||
  ret void
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								transform/testdata/stringtobytes.out.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										10
									
								
								transform/testdata/stringtobytes.out.ll
									
										
									
									
										предоставленный
									
									
								
							| 
						 | 
				
			
			@ -22,3 +22,13 @@ entry:
 | 
			
		|||
  call fastcc void @writeToSlice(ptr %1, i64 6, i64 6)
 | 
			
		||||
  ret void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
define void @testReadSome() {
 | 
			
		||||
entry:
 | 
			
		||||
  %s = call fastcc { ptr, i64, i64 } @runtime.stringToBytes(ptr @str, i64 6)
 | 
			
		||||
  %s.ptr = extractvalue { ptr, i64, i64 } %s, 0
 | 
			
		||||
  call fastcc void @writeToSlice(ptr %s.ptr, i64 6, i64 6)
 | 
			
		||||
  %s.ptr2 = extractvalue { ptr, i64, i64 } %s, 0
 | 
			
		||||
  call fastcc void @printSlice(ptr %s.ptr2, i64 6, i64 6)
 | 
			
		||||
  ret void
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче